Move sources to src and build objs in obj.

This commit is contained in:
Arthur Britto
2012-03-06 22:43:06 -08:00
parent 82af29a21d
commit c513e45754
95 changed files with 55 additions and 39 deletions

54
src/AccountState.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include <boost/lexical_cast.hpp>
#include "AccountState.h"
#include "Serializer.h"
AccountState::AccountState(const std::vector<unsigned char>& v)
{
Serializer s(v);
mValid=false;
if(!s.get160(mAccountID, 0)) { assert(false); return; }
if(!s.get64(mBalance, 20)) { assert(false); return; }
if(!s.get32(mAccountSeq, 28)) { assert(false); return; }
mValid=true;
}
AccountState::AccountState(const uint160& id) : mAccountID(id), mBalance(0), mAccountSeq(0), mValid(true)
{ ; }
std::vector<unsigned char> AccountState::getRaw() const
{ // 20-byte acct ID, 8-byte balance, 4-byte sequence
Serializer s(32);
s.add160(mAccountID);
s.add64(mBalance);
s.add32(mAccountSeq);
return s.getData();
}
static bool isHex(char j)
{
if((j>='0') && (j<='9')) return true;
if((j>='A') && (j<='F')) return true;
if((j>='a') && (j<='f')) return true;
return false;
}
bool AccountState::isHexAccountID(const std::string& acct)
{
if(acct.size()!=40) return false;
for(int i=1; i<40; i++)
if(!isHex(acct[i])) return false;
return true;
}
void AccountState::addJson(Json::Value& val)
{
Json::Value as(Json::objectValue);
as["Account"]=mAccountID.GetHex();
as["Balance"]=boost::lexical_cast<std::string>(mBalance);
as["SendSequence"]=mAccountSeq;
if(!mValid) as["Invalid"]=true;
NewcoinAddress nad(mAccountID);
val[nad.GetString()]=as;
}

59
src/AccountState.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef __ACCOUNTSTATE__
#define __ACCOUNTSTATE__
// An account's state in one or more accepted ledgers
#include <vector>
#include <boost/shared_ptr.hpp>
#include "../json/value.h"
#include "types.h"
#include "uint256.h"
class AccountState
{
public:
typedef boost::shared_ptr<AccountState> pointer;
private:
uint160 mAccountID;
uint64 mBalance;
uint32 mAccountSeq;
bool mValid;
public:
AccountState(const uint160& mAccountID); // new account
AccountState(const std::vector<unsigned char>&); // raw form
const uint160& getAccountID() const { return mAccountID; }
uint64 getBalance() const { return mBalance; }
uint32 getSeq() const { return mAccountSeq; }
void credit(const uint64& a)
{
mBalance+=a;
if(!mAccountSeq) mAccountSeq=1; // an account with non-0 balance cannot have 0 sequence
}
void charge(const uint64& a)
{
assert(mBalance>=a);
mBalance-=a;
}
void incSeq()
{
mAccountSeq++;
}
void decSeq()
{
assert(mAccountSeq!=0);
mAccountSeq--;
}
static bool isHexAccountID(const std::string& acct);
std::vector<unsigned char> getRaw() const;
void addJson(Json::Value& value);
};
#endif

107
src/Application.cpp Normal file
View File

@@ -0,0 +1,107 @@
#include <iostream>
//#include <boost/log/trivial.hpp>
#include "../database/SqliteDatabase.h"
#include "Application.h"
#include "Config.h"
#include "PeerDoor.h"
#include "RPCDoor.h"
#include "BitcoinUtil.h"
#include "key.h"
Application* theApp=NULL;
/*
What needs to happen:
Listen for connections
Try to maintain the right number of connections
Process messages from peers
Process messages from RPC
Periodically publish a new ledger
Save the various pieces of data
*/
DatabaseCon::DatabaseCon(const std::string& name, const char *initStrings[], int initCount)
{
std::string path=strprintf("%s%s", theConfig.DATA_DIR.c_str(), name.c_str());
mDatabase=new SqliteDatabase(path.c_str());
mDatabase->connect();
for(int i=0; i<initCount; i++)
mDatabase->executeSQL(initStrings[i], true);
}
DatabaseCon::~DatabaseCon()
{
mDatabase->disconnect();
delete mDatabase;
}
Application::Application() :
mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL),
mPeerDoor(NULL), mRPCDoor(NULL)
{
theConfig.load();
}
extern const char *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], *NetNodeDBInit[];
extern int TxnDBCount, LedgerDBCount, WalletDBCount, HashNodeDBCount, NetNodeDBCount;
void Application::run()
{
assert(mTxnDB==NULL);
mTxnDB=new DatabaseCon("transaction.db", TxnDBInit, TxnDBCount);
mLedgerDB=new DatabaseCon("ledger.db", LedgerDBInit, LedgerDBCount);
mWalletDB=new DatabaseCon("wallet.db", WalletDBInit, WalletDBCount);
mHashNodeDB=new DatabaseCon("hashnode.db", HashNodeDBInit, HashNodeDBCount);
mNetNodeDB=new DatabaseCon("netnode.db", NetNodeDBInit, NetNodeDBCount);
if(theConfig.PEER_PORT)
{
mPeerDoor=new PeerDoor(mIOService);
}//else BOOST_LOG_TRIVIAL(info) << "No Peer Port set. Not listening for connections.";
if(theConfig.RPC_PORT)
{
mRPCDoor=new RPCDoor(mIOService);
}//else BOOST_LOG_TRIVIAL(info) << "No RPC Port set. Not listening for commands.";
mConnectionPool.connectToNetwork(mKnownNodes, mIOService);
mTimingService.start(mIOService);
std::cout << "Before Run." << std::endl;
// Temporary root account will be ["This is my payphrase."]:0
uint160 rootAddress=NewcoinAddress("MoXdEKxkG1FEwyuMia6Mbrja1SohefRvro").GetHash160();
Ledger::pointer firstLedger(new Ledger(rootAddress, 100000000));
firstLedger->setClosed();
firstLedger->setAccepted();
mMasterLedger.pushLedger(firstLedger);
Ledger::pointer secondLedger=firstLedger->closeLedger(time(NULL));
mMasterLedger.pushLedger(secondLedger);
mMasterLedger.setSynced();
// temporary
mWallet.load();
mWallet.syncToLedger(true, &(*secondLedger));
// temporary
mIOService.run(); // This blocks
//BOOST_LOG_TRIVIAL(info) << "Done.";
std::cout << "Done." << std::endl;
}
Application::~Application()
{
delete mTxnDB;;
delete mLedgerDB;
delete mWalletDB;
delete mHashNodeDB;
delete mNetNodeDB;
}

94
src/Application.h Normal file
View File

@@ -0,0 +1,94 @@
#ifndef __APPLICATION__
#define __APPLICATION__
#include "UniqueNodeList.h"
#include "ConnectionPool.h"
#include "KnownNodeList.h"
#include "TimingService.h"
#include "PubKeyCache.h"
#include "ScopedLock.h"
#include "LedgerMaster.h"
#include "TransactionMaster.h"
#include "Wallet.h"
#include "Peer.h"
#include "NetworkOPs.h"
#include "../database/database.h"
#include <boost/asio.hpp>
class RPCDoor;
class PeerDoor;
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
{
NetworkOPs mNetOps;
Wallet mWallet;
TimingService mTimingService;
UniqueNodeList mUNL;
KnownNodeList mKnownNodes;
PubKeyCache mPKCache;
LedgerMaster mMasterLedger;
TransactionMaster mMasterTransaction;
DatabaseCon *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB;
ConnectionPool mConnectionPool;
PeerDoor* mPeerDoor;
RPCDoor* mRPCDoor;
std::map<std::string, Peer::pointer> mPeerMap;
boost::recursive_mutex mPeerMapLock;
boost::asio::io_service mIOService;
public:
Application();
~Application();
ConnectionPool& getConnectionPool() { return mConnectionPool; }
UniqueNodeList& getUNL() { return mUNL; }
Wallet& getWallet() { return mWallet ; }
NetworkOPs& getOPs() { return mNetOps; }
PubKeyCache& getPubKeyCache() { return mPKCache; }
boost::asio::io_service& getIOService() { return mIOService; }
LedgerMaster& getMasterLedger() { return mMasterLedger; }
TransactionMaster& getMasterTransaction() { return mMasterTransaction; }
DatabaseCon* getTxnDB() { return mTxnDB; }
DatabaseCon* getLedgerDB() { return mLedgerDB; }
DatabaseCon* getWalletDB() { return mWalletDB; }
DatabaseCon* getHashNodeDB() { return mHashNodeDB; }
DatabaseCon* getNetNodeDB() { return mNetNodeDB; }
//Serializer* getSerializer(){ return(mSerializer); }
//void setSerializer(Serializer* ser){ mSerializer=ser; }
void run();
};
extern Application* theApp;
#endif

37
src/Avalanche.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef __AVALANCHE__
#define __AVALANCHE__
#include <map>
#include <set>
#include "Transaction.h"
#include "Hanko.h"
class DisputedTransaction
{
protected:
Transaction::pointer mTransaction;
std::vector<Hanko::pointer> mNodesIncluding;
std::vector<Hanko::pointer> mNodesRejecting;
uint64 mTimeTaken; // when we took our position on this transaction
bool mOurPosition;
};
class DTComp
{
public:
bool operator()(const DisputedTransaction&, const DisputedTransaction&);
};
class Avalanche
{
protected:
SHAMap::pointer mOurLedger;
std::map<uint256, DisputedTransaction:pointer> mTxByID;
std::set<DisputedTransaction::pointer, DTComp> mTxInASOrder;
public:
Avalanche(SHAMap::pointer ourLedger);
};
#endif

31
src/BinaryFormats.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef __BINARYFORMATS__
#define __BINARYFORMATS__
// binary transaction
const int BTxSize=145;
const int BTxPDestAcct=0, BTxLDestAact=20; // destination account pubkey Hash160
const int BTxPAmount=20, BTxLAmount=8; // amount
const int BTxPSASeq=28, BTxLASeq=4; // source account sequence number
const int BTxPSLIdx=32, BTxLSLIdx=4; // source ledger index
const int BTxPSTag=36, BTxLSTag=4; // source tag
const int BTxPSPubK=40, BTxLSPubK=33; // source public key
const int BTxPSig=73, BTxLSig=72; // signature
// ledger (note: fields after the timestamp are not part of the hash)
const int BLgSize=192;
const int BLgPIndex=0, BLgLIndex=4; // ledger index
const int BLgPFeeHeld=4, BLgLFeeHeld=8; // transaction fees held
const int BLgPPrevLg=12, BLgLPrevLg=32; // previous ledger hash
const int BLgPTxT=44, BLgLTxT=32; // transaction tree hash
const int BLgPAcT=76, BLgLPAct=32; // account state hash
const int BLgPClTs=108, BLgLClTs=8; // closing timestamp
const int BLgPConf=116, BLgLPConf=4; // confidence
const int BLgPSig=120, BLgLSig=72; // signature
// account status
const int BAsSize=32;
const int BAsPID=0, BAsLID=20; // account pubkey Hash160
const int BAsPBalance=20, BAsLBalance=8; // account balance
const int BAsPSequence=28, BASLSequence=4; // account sequence
#endif

107
src/BitcoinUtil.cpp Normal file
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
}

106
src/BitcoinUtil.h Normal file
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

172
src/CallRPC.cpp Normal file
View File

@@ -0,0 +1,172 @@
#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"
using namespace boost::asio;
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.c_str(), 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(int argc, char *argv[])
{
std::string strPrint;
int nRet = 0;
try
{
// Skip switches
while(argc > 1 && isSwitchChar(argv[1][0]))
{
argc--;
argv++;
}
if(argc < 2) return(0);
std::string strMethod = argv[1];
// Parameters default to strings
Json::Value params(Json::arrayValue);
for (int i = 2; i < argc; i++)
params.append(argv[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 == "" && theConfig.RPC_PASSWORD == "")
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 port:" << theConfig.RPC_PORT << std::endl;
ip::tcp::endpoint endpoint( ip::address::from_string("127.0.0.1"), theConfig.RPC_PORT);
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
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;
}

7
src/CallRPC.h Normal file
View File

@@ -0,0 +1,7 @@
#include <string>
#include "../json/value.h"
extern int commandLineRPC(int argc, char *argv[]);
extern Json::Value callRPC(const std::string& strMethod, const Json::Value& params);

53
src/Config.cpp Normal file
View File

@@ -0,0 +1,53 @@
#include "Config.h"
#include "../util/pugixml.hpp"
#include <boost/lexical_cast.hpp>
using namespace pugi;
Config theConfig;
Config::Config()
{
VERSION=1;
NETWORK_START_TIME=1319844908;
PEER_PORT=6561;
RPC_PORT=5001;
NUMBER_CONNECTIONS=30;
// a new ledger every 30 min
LEDGER_SECONDS=(60*30);
RPC_USER="admin";
RPC_PASSWORD="pass";
DATA_DIR="db/";
TRANSACTION_FEE_BASE=1000;
}
void Config::load()
{
xml_document doc;
xml_parse_result result = doc.load_file("config.xml");
xml_node root=doc.child("config");
xml_node node= root.child("PEER_PORT");
if(!node.empty()) PEER_PORT=boost::lexical_cast<int>(node.child_value());
node= root.child("RPC_PORT");
if(!node.empty()) RPC_PORT=boost::lexical_cast<int>(node.child_value());
/*
node=root.child("DB_TYPE");
if(!node.empty())
{
if( stricmp(node.child_value(),"mysql")==0 ) theApp->setDB(Database::newMysqlDatabase("host","user","pass"));
else theApp->setSerializer(new DiskSerializer());
}else */
}

46
src/Config.h Normal file
View File

@@ -0,0 +1,46 @@
#include "string"
class Config
{
public:
// core software parameters
int VERSION;
std::string VERSION_STR;
// network parameters
std::string NETWORK_ID;
std::string NETWORK_DNS_SEEDS;
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;
int BELIEF_QUORUM;
float BELIEF_PERCENT;
// node networking parameters
int PEER_PORT;
int NUMBER_CONNECTIONS;
bool NODE_INBOUND; // we accept inbound connections
bool NODE_DATABASE; // we offer historical data services
bool NODE_PUBLIC; // we do not attempt to hide our identity
bool NODE_DUMB; // we are a 'dumb' client
bool NODE_SMART; // we offer services to 'dumb' clients
std::string HANKO_PRIVATE;
// RPC parameters
int RPC_PORT;
std::string RPC_USER;
std::string RPC_PASSWORD;
// configuration parameters
std::string DATA_DIR;
Config();
void load();
};
extern Config theConfig;

44
src/Confirmation.h Normal file
View File

@@ -0,0 +1,44 @@
#ifndef __CONFIRMATION__
#define __CONFIRMATION__
#include "../obj/src/newcoin.pb.h"
#include "uint256.h"
#include <boost/shared_ptr.hpp>
enum ConfirmationStatus
{
NEW, // first for this account/seq
CONFLICTED, // rejected as of this time
ACCEPTED, // in active bundle, has confirmations
COMMITTED
};
class Confirmation
{ // used primarily to report conflicted or rejected transactions
public:
typedef boost::shared_ptr<Transaction> pointer;
private:
uint256 mTransactionID;
uint160 mHanko;
uint64 mTimestamp;
ConfirmationStatus mStatus;
bool mConflicts;
std::vector<unsigned char> mSignature;
public:
Transaction();
Transaction(const uint256 &id);
Transaction(const std::vector<unsigned char> rawTransaction);
const uint256& GetID() const { return mTransactionID; }
const uint160& GetHanko() const { return mHanko; }
uint64 GetTimestamp() const { return mTimestamp; }
ConfirmationStatus() const { return mStatus; }
bool HasConflicts() const { return mConflicts; }
bool save();
};
#endif

110
src/ConnectionPool.cpp Normal file
View File

@@ -0,0 +1,110 @@
#include <boost/foreach.hpp>
#include <boost/asio.hpp>
#include "ConnectionPool.h"
#include "Config.h"
#include "KnownNodeList.h"
#include "Peer.h"
#include "Application.h"
ConnectionPool::ConnectionPool()
{ ; }
void ConnectionPool::connectToNetwork(KnownNodeList& nodeList,boost::asio::io_service& io_service)
{
for(int n=0; n<theConfig.NUMBER_CONNECTIONS; n++)
{
KnownNode* node=nodeList.getNextNode();
if(!node) return;
Peer::pointer peer=Peer::create(io_service);
// peer->connectTo(*node); // FIXME
mPeers.push_back(peer);
}
}
/*
bool ConnectionPool::isMessageKnown(PackedMessage::pointer msg)
{
for(unsigned int n=0; n<mBroadcastMessages.size(); n++)
{
if(msg==mBroadcastMessages[n].first) return(false);
}
return(false);
}
*/
void ConnectionPool::relayMessage(Peer* fromPeer, PackedMessage::pointer msg)
{
BOOST_FOREACH(Peer::pointer peer, mPeers)
{
if(!fromPeer || !(peer.get() == fromPeer))
peer->sendPacket(msg);
}
}
bool ConnectionPool::addToMap(const uint160& hanko, Peer::pointer peer)
{
boost::mutex::scoped_lock sl(peerLock);
return peerMap.insert(std::make_pair(hanko, peer)).second;
}
bool ConnectionPool::delFromMap(const uint160& hanko)
{
boost::mutex::scoped_lock sl(peerLock);
std::map<uint160, Peer::pointer>::iterator it=peerMap.find(hanko);
if((it==peerMap.end()) || (it->first!=hanko)) return false;
peerMap.erase(it);
return true;
}
Peer::pointer ConnectionPool::findInMap(const uint160& hanko)
{
boost::mutex::scoped_lock sl(peerLock);
std::map<uint160, Peer::pointer>::iterator it=peerMap.find(hanko);
if(it==peerMap.end()) return Peer::pointer();
return it->second;
}
bool ConnectionPool::inMap(const uint160& hanko)
{
boost::mutex::scoped_lock sl(peerLock);
return peerMap.find(hanko) != peerMap.end();
}
std::map<uint160, Peer::pointer> ConnectionPool::getAllConnected()
{
boost::mutex::scoped_lock sl(peerLock);
return peerMap;
}
bool ConnectionPool::connectTo(const std::string& host, const std::string& port)
{
try
{
boost::asio::ip::tcp::resolver res(theApp->getIOService());
boost::asio::ip::tcp::resolver::query query(host.c_str(), port.c_str());
boost::asio::ip::tcp::resolver::iterator it(res.resolve(query)), end;
Peer::pointer peer(Peer::create(theApp->getIOService()));
boost::system::error_code error = boost::asio::error::host_not_found;
while (error && (it!=end))
{
peer->getSocket().close();
peer->getSocket().connect(*it++, error);
}
if(error) return false;
boost::mutex::scoped_lock sl(peerLock);
mPeers.push_back(peer);
peer->connected(boost::system::error_code());
}
catch (...)
{
return false;
}
return true;
}

39
src/ConnectionPool.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef __CONNECTION_POOL__
#define __CONNECTION_POOL__
#include <boost/asio.hpp>
#include <boost/thread/mutex.hpp>
#include "Peer.h"
#include "PackedMessage.h"
#include "types.h"
class KnownNodeList;
/*
This is the list of all the Peers we are currently connected to
*/
class ConnectionPool
{
boost::mutex peerLock;
std::vector<Peer::pointer> mPeers; // FIXME
std::map<uint160, Peer::pointer> peerMap;
//std::vector<std::pair<PackedMessage::pointer,int> > mBroadcastMessages;
public:
ConnectionPool();
void connectToNetwork(KnownNodeList& nodeList, boost::asio::io_service& io_service);
void relayMessage(Peer* fromPeer, PackedMessage::pointer msg);
//bool isMessageKnown(PackedMessage::pointer msg);
// hanko->peer mapping functions
bool inMap(const uint160& hanko);
bool addToMap(const uint160& hanko, Peer::pointer peer);
bool delFromMap(const uint160& hanko);
Peer::pointer findInMap(const uint160& hanko);
std::map<uint160, Peer::pointer> getAllConnected();
bool connectTo(const std::string& host, const std::string& port);
};
#endif

53
src/Conversion.cpp Normal file
View File

@@ -0,0 +1,53 @@
#include "Conversion.h"
#include "base58.h"
using namespace std;
uint160 protobufTo160(const std::string& buf)
{
uint160 ret;
// TODO:
return(ret);
}
uint256 protobufTo256(const std::string& hash)
{
uint256 ret;
// TODO:
return(ret);
}
uint160 humanTo160(const std::string& buf)
{
vector<unsigned char> retVec;
DecodeBase58(buf,retVec);
uint160 ret;
memcpy((unsigned char*)&ret,&retVec[0],ret.GetSerializeSize());
return(ret);
}
bool humanToPK(const std::string& buf,std::vector<unsigned char>& retVec)
{
return(DecodeBase58(buf,retVec));
}
bool u160ToHuman(uint160& buf, std::string& retStr)
{
retStr=EncodeBase58(buf.begin(),buf.end());
return(true);
}
base_uint160 uint256::to160() const
{
uint160 m;
memcpy(&m, this, sizeof(uint160));
return m;
}
base_uint256 uint160::to256() const
{
uint256 m;
memcpy(&m, this, sizeof(this));
return m;
}

11
src/Conversion.h Normal file
View File

@@ -0,0 +1,11 @@
#include "uint256.h"
#include <string>
extern uint160 protobufTo160(const std::string& buf);
extern uint256 protobufTo256(const std::string& hash);
extern uint160 humanTo160(const std::string& buf);
extern bool humanToPK(const std::string& buf,std::vector<unsigned char>& retVec);
extern bool u160ToHuman(uint160& buf, std::string& retStr);

100
src/DBInit.cpp Normal file
View File

@@ -0,0 +1,100 @@
#include <string>
// Transaction database holds transactions and public keys
const char *TxnDBInit[] = {
"CREATE TABLE Transactions ( \
TransID CHARACTER(64) PRIMARY KEY, \
FromAcct CHARACTER(40), \
FromSeq BIGINT UNSIGNED, \
FromLedger BIGINT UNSIGNED, \
Identifier BIGINT UNSIGNED, \
ToAcct CHARACTER(40), \
Amount BIGINT UNSIGNED, \
Fee BIGINT UNSIGNED, \
FirstSeen TEXT, \
CommitSeq BIGINT UNSIGNED, \
Status CHARACTER(1), \
Signature BLOB \
);",
"CREATE TABLE PubKeys ( \
ID CHARACTER(40) PRIMARY KEY, \
PubKey BLOB \
);" };
int TxnDBCount=sizeof(TxnDBInit)/sizeof(const char *);
// Ledger database holds ledgers and ledger confirmations
const char *LedgerDBInit[] = {
"CREATE TABLE Ledgers ( \
LedgerHash CHARACTER(64) PRIMARY KEY, \
LedgerSeq BIGINT UNSIGNED, \
PrevHash CHARACTER(64), \
FeeHeld BIGINT UNSIGNED, \
ClosingTime BIGINT UNSINGED, \
AccountSetHash CHARACTER(64), \
TransSetHash CHARACTER(64) \
);",
"CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);",
"CREATE TABLE LedgerConfirmations ( \
LedgerSeq BIGINT UNSIGNED, \
LedgerHash CHARACTER(64), \
Hanko CHARACTER(40), \
Signature BLOB \
);",
"CREATE INDEX LedgerConfByHash ON \
LedgerConfirmations(LedgerHash)l" };
int LedgerDBCount=sizeof(LedgerDBInit)/sizeof(const char *);
// Wallet database holds local accounts and trusted nodes
const char *WalletDBInit[] = {
"CREATE TABLE LocalAcctFamilies ( \
FamilyName CHARACTER(40) PRIMARY KEY, \
RootPubKey CHARACTER(66), \
Seq BIGINT UNSIGNED, \
Name TEXT, \
Comment TEXT \
);",
"CREATE TABLE LocalAccounts ( \
ID CHARACTER(40) PRIMARY KEY, \
KeyType CHARACTER(1), \
PrivateKey TEXT \
Seq BIGINT UNSIGNED, \
Balance BIGINT UNSIGNED, \
LedgerSeq BIGINT UNSIGNED, \
Name TEXT, \
Comment TEXT \
);",
"CREATE TABLE TrustedNodes ( ` \
Hanko CHARACTER(40) PRIMARY KEY, \
TrustLevel SMALLINT, \
Comment TEXT \
);" };
int WalletDBCount=sizeof(WalletDBInit)/sizeof(const char *);
// Hash node database holds nodes indexed by hash
const char *HashNodeDBInit[] = {
"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);" };
int HashNodeDBCount=sizeof(HashNodeDBInit)/sizeof(const char *);
// Net node database holds nodes seen on the network
const char *NetNodeDBInit[] = {
"CREATE TABLE KnownNodes ( \
Hanko CHARACTER(40) PRIMARY KEY, \
LastSeen TEXT, \
HaveContactInfo CHARACTER(1), \
ContactObject BLOB \
);" };
int NetNodeDBCount=sizeof(NetNodeDBInit)/sizeof(const char *);

275
src/DeterministicKeys.cpp Normal file
View File

@@ -0,0 +1,275 @@
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <openssl/ecdsa.h>
#include <openssl/pem.h>
// Functions to add CKey support for deterministic EC keys
#include "Serializer.h"
uint256 CKey::PassPhraseToKey(const std::string& passPhrase)
{
Serializer s;
s.addRaw(passPhrase.c_str(), passPhrase.size());
uint256 ret(s.getSHA512Half());
s.secureErase();
return ret;
}
EC_KEY* CKey::GenerateRootDeterministicKey(const uint256& key)
{
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(72);
s.add256(key);
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_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_free(privKey);
EC_POINT_free(pubKey);
EC_KEY_free(pkey);
BN_CTX_free(ctx);
return NULL;
}
BN_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);
assert(EC_KEY_check_key(pkey)==1);
return pkey;
}
EC_KEY* CKey::GenerateRootPubKey(const std::string& pubHex)
{
BIGNUM* bn=NULL;
BN_hex2bn(&bn, pubHex.c_str());
if(bn==NULL) return NULL;
EC_KEY* pkey=EC_KEY_new_by_curve_name(NID_secp256k1);
if(!pkey)
{
BN_free(bn);
return NULL;
}
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
EC_POINT* pubPoint=EC_POINT_bn2point(EC_KEY_get0_group(pkey), bn, NULL, NULL);
BN_free(bn);
if(!pubPoint)
{
EC_KEY_free(pkey);
return NULL;
}
if(!EC_KEY_set_public_key(pkey, pubPoint))
{
EC_POINT_free(pubPoint);
EC_KEY_free(pkey);
return NULL;
}
return pkey;
}
static BIGNUM* makeHash(const uint160& family, int seq, BIGNUM* order)
{
int subSeq=0;
BIGNUM* ret=NULL;
do
{
Serializer s(28);
s.add160(family);
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;
}
EC_KEY* CKey::GeneratePublicDeterministicKey(const uint160& family, const EC_POINT* rootPubKey, int seq)
{ // publicKey(n) = rootPublicKey EC_POINT_+ Hash(pubHash|seq)*point
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);
EC_POINT *newPoint=EC_POINT_new(EC_KEY_get0_group(pkey));
if(newPoint==NULL)
{
EC_KEY_free(pkey);
BN_CTX_free(ctx);
return NULL;
}
BIGNUM* order=BN_new();
if(!order || !EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
{
if(order) BN_free(order);
EC_KEY_free(pkey);
BN_CTX_free(ctx);
return NULL;
}
// calculate the private additional key
BIGNUM* hash=makeHash(family, seq, order);
BN_free(order);
if(hash==NULL)
{
EC_POINT_free(newPoint);
BN_CTX_free(ctx);
EC_KEY_free(pkey);
return NULL;
}
// calculate the corresponding public key
EC_POINT_mul(EC_KEY_get0_group(pkey), newPoint, hash, NULL, NULL, ctx);
BN_free(hash);
// 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);
EC_POINT_free(newPoint);
BN_CTX_free(ctx);
return pkey;
}
EC_KEY* CKey::GeneratePrivateDeterministicKey(const uint160& family, 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(family, 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_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_free(privKey);
BN_CTX_free(ctx);
EC_KEY_free(pkey);
return NULL;
}
BN_free(privKey);
EC_KEY_set_public_key(pkey, pubKey);
EC_POINT_free(pubKey);
BN_CTX_free(ctx);
return pkey;
}

11
src/Hanko.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include "Hanko.h"
#include <boost/foreach.hpp>
using namespace boost;
using namespace std;
Hanko::Hanko()
{
}

64
src/Hanko.h Normal file
View File

@@ -0,0 +1,64 @@
#ifndef __HANKO__
#define __HANKO__
// We use SECP256K1 http://www.secg.org/collateral/sec2_final.pdf
#include "key.h"
enum HankoFormat
{
TEXT, // Hanko in text form
RAW, // Hanko in raw binary form
CONTACT, // Hanko contact block
};
class Hanko
{
public:
static const int smPubKeySize= 65;
static const int smPrivKeySize = 279;
static const int smSigSize = 57;
private:
std::string mHanko;
std::vector<unsigned char> mContactBlock;
CKey mPubKey;
public:
Hanko();
Hanko(const std::string& TextHanko);
Hanko(const std::vector<unsigned char>& Data, HankoFormat format);
Hanko(const CKey &pubKey);
Hanko(const Hanko &);
std::string GetHankoString(HankoFormat format) const;
std::vector<unsigned char> GetHankoBinary(HankoFormat format) const;
const std::vector<unsigned char>& GetContactBlock() const { return mContactBlock; }
const CKey& GetPublicKey() const { return mPubKey; }
int UpdateContact(std::vector<unsigned char>& Contact);
bool CheckHashSign(const uint256& hash, const std::vector<unsigned char>& Signature);
bool CheckPrefixSign(const std::vector<unsigned char>& data, uint64 type,
const std::vector<unsigned char> &signature);
};
class LocalHanko : public Hanko
{
private:
CKey mPrivKey;
public:
LocalHanko(std::vector<unsigned char> &PrivKey);
LocalHanko(const CKey &Privkey);
LocalHanko(const LocalHanko &);
~LocalHanko();
bool HashSign(const uint256& hash, std::vector<unsigned char>& Signature);
bool PrefixSign(std::vector<unsigned char> data, uint64 type, std::vector<unsigned char> &Signature);
};
#endif

124
src/HashedObject.cpp Normal file
View File

@@ -0,0 +1,124 @@
#include "boost/lexical_cast.hpp"
#include "HashedObject.h"
#include "Serializer.h"
#include "Application.h"
bool HashedObject::checkHash() const
{
uint256 hash=Serializer::getSHA512Half(mData);
return hash==mHash;
}
bool HashedObject::checkFixHash()
{
uint256 hash=Serializer::getSHA512Half(mData);
if(hash==mHash) return true;
mHash=hash;
return false;
}
void HashedObject::setHash()
{
mHash=Serializer::getSHA512Half(mData);
}
/*
CREATE TABLE CommittedObjects ( -- used to synch nodes
Hash BLOB PRIMARY KEY,
ObjType CHAR(1) NOT NULL, -- (L)edger, (T)ransaction, (A)ccount node, transaction (N)ode
LedgerIndex BIGINT UNSIGNED, -- 0 if none
Object BLOB
);
CREATE INDEX ObjectLocate ON CommittedObjects(LedgerIndex, ObjType);
*/
bool HashedObject::store(HashedObjectType type, uint32 index, const std::vector<unsigned char>& data,
const uint256& hash)
{
if(!theApp->getHashNodeDB()) return true;
#ifdef DEBUG
Serializer s(data);
assert(hash==s.getSHA512Half());
#endif
std::string sql="INSERT INTO CommitedObjects (Hash,ObjType,LedgerIndex,Object) VALUES ('";
sql.append(hash.GetHex());
switch(type)
{
case LEDGER: sql.append("','L','"); break;
case TRANSACTION: sql.append("','T','"); break;
case ACCOUNT_NODE: sql.append("','A','"); break;
case TRANSACTION_NODE: sql.append("','N','"); break;
default: sql.append("','U','"); break;
}
sql.append(boost::lexical_cast<std::string>(index));
sql.append("',");
std::string obj;
theApp->getHashNodeDB()->getDB()->escape(&(data.front()), data.size(), obj);
sql.append(obj);
sql.append(");");
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
Database* db=theApp->getHashNodeDB()->getDB();
return db->executeSQL(sql.c_str());
}
bool HashedObject::store() const
{
#ifdef DEBUG
assert(checkHash());
#endif
return store(mType, mLedgerIndex, mData, mHash);
}
HashedObject::pointer HashedObject::retrieve(const uint256& hash)
{
if(!theApp->getHashNodeDB()) return HashedObject::pointer();
std::string sql="SELECT * from CommitedObjects WHERE Hash='";
sql.append(hash.GetHex());
sql.append("';");
std::string type;
uint32 index;
std::vector<unsigned char> data;
data.reserve(8192);
if(1)
{
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
Database* db=theApp->getHashNodeDB()->getDB();
if(!db->executeSQL(sql.c_str()) || !db->startIterRows() || !db->getNextRow())
return HashedObject::pointer();
std::string type;
db->getStr("ObjType", type);
if(type.size()==0) return HashedObject::pointer();
index=db->getBigInt("LedgerIndex");
int size=db->getBinary("Object", NULL, 0);
data.resize(size);
db->getBinary("Object", &(data.front()), size);
db->endIterRows();
}
HashedObjectType htype=UNKNOWN;
switch(type[0])
{
case 'L': htype=LEDGER; break;
case 'T': htype=TRANSACTION; break;
case 'A': htype=ACCOUNT_NODE; break;
case 'N': htype=TRANSACTION_NODE; break;
}
HashedObject::pointer obj(new HashedObject(htype, index, data));
obj->mHash=hash;
#ifdef DEBUG
assert(obj->checkHash());
#endif
return obj;
}

46
src/HashedObject.h Normal file
View File

@@ -0,0 +1,46 @@
#ifndef __HASHEDOBJECT__
#define __HASHEDOBJECT__
#include <vector>
#include <boost/shared_ptr.hpp>
#include "types.h"
#include "uint256.h"
enum HashedObjectType
{
UNKNOWN=0,
LEDGER=1,
TRANSACTION=2,
ACCOUNT_NODE=3,
TRANSACTION_NODE=4
};
class 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) :
mType(type), mLedgerIndex(index), mData(data) { ; }
bool checkHash() const;
bool checkFixHash();
void setHash();
const std::vector<unsigned char>& getData() { return mData; }
bool store() const;
static bool store(HashedObjectType type, uint32 index, const std::vector<unsigned char>& data,
const uint256& hash);
static HashedObject::pointer retrieve(const uint256& hash);
};
#endif

241
src/HttpReply.cpp Normal file
View File

@@ -0,0 +1,241 @@
#include "HttpReply.h"
#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
namespace status_strings
{
const std::string ok =
"HTTP/1.0 200 OK\r\n";
const std::string created =
"HTTP/1.0 201 Created\r\n";
const std::string accepted =
"HTTP/1.0 202 Accepted\r\n";
const std::string no_content =
"HTTP/1.0 204 No Content\r\n";
const std::string multiple_choices =
"HTTP/1.0 300 Multiple Choices\r\n";
const std::string moved_permanently =
"HTTP/1.0 301 Moved Permanently\r\n";
const std::string moved_temporarily =
"HTTP/1.0 302 Moved Temporarily\r\n";
const std::string not_modified =
"HTTP/1.0 304 Not Modified\r\n";
const std::string bad_request =
"HTTP/1.0 400 Bad Request\r\n";
const std::string unauthorized =
"HTTP/1.0 401 Unauthorized\r\n";
const std::string forbidden =
"HTTP/1.0 403 Forbidden\r\n";
const std::string not_found =
"HTTP/1.0 404 Not Found\r\n";
const std::string internal_server_error =
"HTTP/1.0 500 Internal Server Error\r\n";
const std::string not_implemented =
"HTTP/1.0 501 Not Implemented\r\n";
const std::string bad_gateway =
"HTTP/1.0 502 Bad Gateway\r\n";
const std::string service_unavailable =
"HTTP/1.0 503 Service Unavailable\r\n";
boost::asio::const_buffer to_buffer(HttpReply::status_type status)
{
switch (status)
{
case HttpReply::ok:
return boost::asio::buffer(ok);
case HttpReply::created:
return boost::asio::buffer(created);
case HttpReply::accepted:
return boost::asio::buffer(accepted);
case HttpReply::no_content:
return boost::asio::buffer(no_content);
case HttpReply::multiple_choices:
return boost::asio::buffer(multiple_choices);
case HttpReply::moved_permanently:
return boost::asio::buffer(moved_permanently);
case HttpReply::moved_temporarily:
return boost::asio::buffer(moved_temporarily);
case HttpReply::not_modified:
return boost::asio::buffer(not_modified);
case HttpReply::bad_request:
return boost::asio::buffer(bad_request);
case HttpReply::unauthorized:
return boost::asio::buffer(unauthorized);
case HttpReply::forbidden:
return boost::asio::buffer(forbidden);
case HttpReply::not_found:
return boost::asio::buffer(not_found);
case HttpReply::internal_server_error:
return boost::asio::buffer(internal_server_error);
case HttpReply::not_implemented:
return boost::asio::buffer(not_implemented);
case HttpReply::bad_gateway:
return boost::asio::buffer(bad_gateway);
case HttpReply::service_unavailable:
return boost::asio::buffer(service_unavailable);
default:
return boost::asio::buffer(internal_server_error);
}
}
} // namespace status_strings
namespace misc_strings
{
const char name_value_separator[] = { ':', ' ' };
const char crlf[] = { '\r', '\n' };
} // namespace misc_strings
std::vector<boost::asio::const_buffer> HttpReply::to_buffers()
{
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(status_strings::to_buffer(status));
for (std::size_t i = 0; i < headers.size(); ++i)
{
HttpHeader& h = headers[i];
buffers.push_back(boost::asio::buffer(h.name));
buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
buffers.push_back(boost::asio::buffer(h.value));
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
}
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
buffers.push_back(boost::asio::buffer(content));
return buffers;
}
namespace stock_replies {
const char ok[] = "";
const char created[] =
"<html>"
"<head><title>Created</title></head>"
"<body><h1>201 Created</h1></body>"
"</html>";
const char accepted[] =
"<html>"
"<head><title>Accepted</title></head>"
"<body><h1>202 Accepted</h1></body>"
"</html>";
const char no_content[] =
"<html>"
"<head><title>No Content</title></head>"
"<body><h1>204 Content</h1></body>"
"</html>";
const char multiple_choices[] =
"<html>"
"<head><title>Multiple Choices</title></head>"
"<body><h1>300 Multiple Choices</h1></body>"
"</html>";
const char moved_permanently[] =
"<html>"
"<head><title>Moved Permanently</title></head>"
"<body><h1>301 Moved Permanently</h1></body>"
"</html>";
const char moved_temporarily[] =
"<html>"
"<head><title>Moved Temporarily</title></head>"
"<body><h1>302 Moved Temporarily</h1></body>"
"</html>";
const char not_modified[] =
"<html>"
"<head><title>Not Modified</title></head>"
"<body><h1>304 Not Modified</h1></body>"
"</html>";
const char bad_request[] =
"<html>"
"<head><title>Bad Request</title></head>"
"<body><h1>400 Bad Request</h1></body>"
"</html>";
const char unauthorized[] =
"<html>"
"<head><title>Unauthorized</title></head>"
"<body><h1>401 Unauthorized</h1></body>"
"</html>";
const char forbidden[] =
"<html>"
"<head><title>Forbidden</title></head>"
"<body><h1>403 Forbidden</h1></body>"
"</html>";
const char not_found[] =
"<html>"
"<head><title>Not Found</title></head>"
"<body><h1>404 Not Found</h1></body>"
"</html>";
const char internal_server_error[] =
"<html>"
"<head><title>Internal Server Error</title></head>"
"<body><h1>500 Internal Server Error</h1></body>"
"</html>";
const char not_implemented[] =
"<html>"
"<head><title>Not Implemented</title></head>"
"<body><h1>501 Not Implemented</h1></body>"
"</html>";
const char bad_gateway[] =
"<html>"
"<head><title>Bad Gateway</title></head>"
"<body><h1>502 Bad Gateway</h1></body>"
"</html>";
const char service_unavailable[] =
"<html>"
"<head><title>Service Unavailable</title></head>"
"<body><h1>503 Service Unavailable</h1></body>"
"</html>";
std::string to_string(HttpReply::status_type status)
{
switch (status)
{
case HttpReply::ok:
return ok;
case HttpReply::created:
return created;
case HttpReply::accepted:
return accepted;
case HttpReply::no_content:
return no_content;
case HttpReply::multiple_choices:
return multiple_choices;
case HttpReply::moved_permanently:
return moved_permanently;
case HttpReply::moved_temporarily:
return moved_temporarily;
case HttpReply::not_modified:
return not_modified;
case HttpReply::bad_request:
return bad_request;
case HttpReply::unauthorized:
return unauthorized;
case HttpReply::forbidden:
return forbidden;
case HttpReply::not_found:
return not_found;
case HttpReply::internal_server_error:
return internal_server_error;
case HttpReply::not_implemented:
return not_implemented;
case HttpReply::bad_gateway:
return bad_gateway;
case HttpReply::service_unavailable:
return service_unavailable;
default:
return internal_server_error;
}
}
} // namespace stock_replies
HttpReply HttpReply::stock_reply(HttpReply::status_type status)
{
HttpReply rep;
rep.status = status;
rep.content = stock_replies::to_string(status);
rep.headers.resize(2);
rep.headers[0].name = "Content-Length";
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
rep.headers[1].name = "Content-Type";
rep.headers[1].value = "text/html";
return rep;
}

50
src/HttpReply.h Normal file
View File

@@ -0,0 +1,50 @@
#ifndef HTTP_REPLY_HPP
#define HTTP_REPLY_HPP
#include <string>
#include <vector>
#include <boost/asio.hpp>
#include "HttpRequest.h"
/// A reply to be sent to a client.
class HttpReply
{
public:
/// The status of the reply.
enum 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
} status;
/// The headers to be included in the reply.
std::vector<HttpHeader> headers;
/// The content to be sent in the reply.
std::string content;
/// Convert the reply into a vector of buffers. The buffers do not own the
/// underlying memory blocks, therefore the reply object must remain valid and
/// not be changed until the write operation has completed.
std::vector<boost::asio::const_buffer> to_buffers();
/// Get a stock reply.
static HttpReply stock_reply(status_type status);
};
#endif // HTTP_REPLY_HPP

24
src/HttpRequest.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef HTTP_REQUEST_HPP
#define HTTP_REQUEST_HPP
#include <string>
#include <vector>
struct HttpHeader
{
std::string name;
std::string value;
};
/// A request received from a client.
struct HttpRequest
{
std::string method;
std::string uri;
int http_version_major;
int http_version_minor;
std::vector<HttpHeader> headers;
std::string mBody;
};
#endif // HTTP_REQUEST_HPP

38
src/KnownNodeList.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include "KnownNodeList.h"
#include "../util/pugixml.hpp"
using namespace pugi;
KnownNode::KnownNode(const char* ip,int port,int lastSeen,int lastTried)
: mIP(ip), mPort(port), mLastSeen(lastSeen), mLastTried(lastTried)
{
}
KnownNodeList::KnownNodeList()
{
mTriedIndex=0;
}
void KnownNodeList::load()
{
xml_document doc;
xml_parse_result result = doc.load_file("nodes.xml");
xml_node nodes=doc.child("nodes");
for(xml_node child = nodes.first_child(); child; child = child.next_sibling())
{
mNodes.push_back(KnownNode(child.attribute("ip").value(),child.attribute("port").as_int(),child.attribute("last").as_int(),0));
}
}
KnownNode* KnownNodeList::getNextNode()
{
if(mTriedIndex>=mNodes.size())
{
return(NULL);
}else
{
mTriedIndex++;
return(&(mNodes[mTriedIndex-1]));
}
}

31
src/KnownNodeList.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef __KNOWNNODELIST__
#define __KNOWNNODELIST__
#include "vector"
#include <string>
class KnownNode
{
public:
std::string mIP;
int mPort;
int mLastSeen;
int mLastTried;
KnownNode(const char* ip,int port,int lastSeen,int lastTried);
};
class KnownNodeList
{
unsigned int mTriedIndex;
std::vector <KnownNode> mNodes;
public:
KnownNodeList();
void load();
void addNode();
KnownNode* getNextNode();
};
#endif

587
src/Ledger.cpp Normal file
View File

@@ -0,0 +1,587 @@
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include "Application.h"
#include "Ledger.h"
#include "../obj/src/newcoin.pb.h"
#include "PackedMessage.h"
#include "Config.h"
#include "Conversion.h"
#include "BitcoinUtil.h"
#include "Wallet.h"
#include "BinaryFormats.h"
Ledger::Ledger(const uint160& masterID, uint64 startAmount) :
mFeeHeld(0), mTimeStamp(0), mLedgerSeq(0),
mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false)
{
mTransactionMap=boost::make_shared<SHAMap>();
mAccountStateMap=boost::make_shared<SHAMap>();
AccountState::pointer startAccount=boost::make_shared<AccountState>(masterID);
startAccount->credit(startAmount);
if(!addAccountState(startAccount))
assert(false);
}
Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
uint64 feeHeld, uint64 timeStamp, uint32 ledgerSeq)
: mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash),
mFeeHeld(feeHeld), mTimeStamp(timeStamp), mLedgerSeq(ledgerSeq),
mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false)
{
updateHash();
}
Ledger::Ledger(Ledger &prevLedger, uint64 ts) : mTimeStamp(ts),
mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false),
mTransactionMap(new SHAMap()), mAccountStateMap(prevLedger.mAccountStateMap)
{
mParentHash=prevLedger.getHash();
mLedgerSeq=prevLedger.mLedgerSeq+1;
mAccountStateMap->setSeq(mLedgerSeq);
}
Ledger::Ledger(const std::vector<unsigned char>& rawLedger) : mFeeHeld(0), mTimeStamp(0),
mLedgerSeq(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true)
{
Serializer s(rawLedger);
// 32seq, 64fee, 256phash, 256thash, 256ahash, 64ts
if(!s.get32(mLedgerSeq, BLgPIndex)) return;
if(!s.get64(mFeeHeld, BLgPFeeHeld)) return;
if(!s.get256(mParentHash, BLgPPrevLg)) return;
if(!s.get256(mTransHash, BLgPTxT)) return;
if(!s.get256(mAccountHash, BLgPAcT)) return;
if(!s.get64(mTimeStamp, BLgPClTs)) return;
updateHash();
if(mValidHash)
{
mTransactionMap=boost::make_shared<SHAMap>();
mAccountStateMap=boost::make_shared<SHAMap>();
}
}
Ledger::Ledger(const std::string& rawLedger) : mFeeHeld(0), mTimeStamp(0),
mLedgerSeq(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true)
{
Serializer s(rawLedger);
// 32seq, 64fee, 256phash, 256thash, 256ahash, 64ts
if(!s.get32(mLedgerSeq, BLgPIndex)) return;
if(!s.get64(mFeeHeld, BLgPFeeHeld)) return;
if(!s.get256(mParentHash, BLgPPrevLg)) return;
if(!s.get256(mTransHash, BLgPTxT)) return;
if(!s.get256(mAccountHash, BLgPAcT)) return;
if(!s.get64(mTimeStamp, BLgPClTs)) return;
updateHash();
if(mValidHash)
{
mTransactionMap=boost::make_shared<SHAMap>();
mAccountStateMap=boost::make_shared<SHAMap>();
}
}
void Ledger::updateHash()
{
if(!mImmutable)
{
if(mTransactionMap) mTransHash=mTransactionMap->getHash();
else mTransHash.zero();
if(mAccountStateMap) mAccountHash=mAccountStateMap->getHash();
else mAccountHash.zero();
}
Serializer s(116);
addRaw(s);
mHash=s.getSHA512Half();
mValidHash=true;
}
void Ledger::addRaw(Serializer &s)
{
s.add32(mLedgerSeq);
s.add64(mFeeHeld);
s.add256(mParentHash);
s.add256(mTransHash);
s.add256(mAccountHash);
s.add64(mTimeStamp);
}
AccountState::pointer Ledger::getAccountState(const uint160& accountID)
{
#ifdef DEBUG
std::cerr << "Ledger:getAccountState(" << accountID.GetHex() << ")" << std::endl;
#endif
ScopedLock l(mTransactionMap->Lock());
SHAMapItem::pointer item=mAccountStateMap->peekItem(accountID.to256());
if(!item)
{
#ifdef DEBUG
std::cerr << " notfound" << std::endl;
#endif
return AccountState::pointer();
}
return boost::make_shared<AccountState>(item->getData());
}
uint64 Ledger::getBalance(const uint160& accountID) const
{
ScopedLock l(mTransactionMap->Lock());
SHAMapItem::pointer item=mAccountStateMap->peekItem(accountID.to256());
if(!item) return 0;
return AccountState(item->getData()).getBalance();
}
bool Ledger::updateAccountState(AccountState::pointer state)
{
assert(!mAccepted);
SHAMapItem::pointer item=boost::make_shared<SHAMapItem>(state->getAccountID(), state->getRaw());
return mAccountStateMap->updateGiveItem(item, false);
}
bool Ledger::addAccountState(AccountState::pointer state)
{
assert(!mAccepted);
assert( (state->getBalance()==0) || (state->getSeq()>0) );
SHAMapItem::pointer item=boost::make_shared<SHAMapItem>(state->getAccountID(), state->getRaw());
return mAccountStateMap->addGiveItem(item, false);
}
bool Ledger::addTransaction(Transaction::pointer trans)
{ // low-level - just add to table
assert(!mAccepted);
assert(!!trans->getID());
SHAMapItem::pointer item=boost::make_shared<SHAMapItem>(trans->getID(), trans->getSigned()->getData());
return mTransactionMap->addGiveItem(item, true);
}
bool Ledger::delTransaction(const uint256& transID)
{
assert(!mAccepted);
return mTransactionMap->delItem(transID);
}
bool Ledger::hasTransaction(const uint256& transID) const
{
return mTransactionMap->hasItem(transID);
}
Transaction::pointer Ledger::getTransaction(const uint256& transID) const
{
SHAMapItem::pointer item=mTransactionMap->peekItem(transID);
if(!item) return Transaction::pointer();
Transaction::pointer txn=theApp->getMasterTransaction().fetch(transID, false);
if(txn) return txn;
txn=boost::make_shared<Transaction>(item->getData(), true);
if(txn->getStatus()==NEW) txn->setStatus(mClosed ? COMMITTED : INCLUDED, mLedgerSeq);
theApp->getMasterTransaction().canonicalize(txn, false);
return txn;
}
Ledger::TransResult Ledger::applyTransaction(Transaction::pointer trans)
{
assert(!mAccepted);
boost::recursive_mutex::scoped_lock sl(mLock);
if(trans->getSourceLedger()>mLedgerSeq) return TR_BADLSEQ;
if(trans->getAmount()<trans->getFee())
{
#ifdef DEBUG
std::cerr << "Transaction for " << trans->getAmount() << ", but fee is " <<
trans->getFee() << std::endl;
#endif
return TR_TOOSMALL;
}
if(!mTransactionMap || !mAccountStateMap) return TR_ERROR;
try
{
// already applied?
Transaction::pointer dupTrans=getTransaction(trans->getID());
if(dupTrans) return TR_ALREADY;
// accounts exist?
AccountState::pointer fromAccount=getAccountState(trans->getFromAccount());
AccountState::pointer toAccount=getAccountState(trans->getToAccount());
// temporary code -- if toAccount doesn't exist but fromAccount does, create it
if(!!fromAccount && !toAccount)
{
toAccount=boost::make_shared<AccountState>(trans->getToAccount());
toAccount->incSeq(); // an account in a ledger has a sequence of 1
updateAccountState(toAccount);
}
if(!fromAccount || !toAccount) return TR_BADACCT;
// pass sanity checks?
if(fromAccount->getBalance()<trans->getAmount())
{
#ifdef DEBUG
std::cerr << "Transaction for " << trans->getAmount() << ", but account has " <<
fromAccount->getBalance() << std::endl;
#endif
return TR_INSUFF;
}
#ifdef DEBUG
if(fromAccount->getSeq()!=trans->getFromAccountSeq())
std::cerr << "aSeq=" << fromAccount->getSeq() << ", tSeq=" << trans->getFromAccountSeq() << std::endl;
#endif
if(fromAccount->getSeq()>trans->getFromAccountSeq()) return TR_PASTASEQ;
if(fromAccount->getSeq()<trans->getFromAccountSeq()) return TR_PREASEQ;
// apply
fromAccount->charge(trans->getAmount());
fromAccount->incSeq();
toAccount->credit(trans->getAmount()-trans->getFee());
mFeeHeld+=trans->getFee();
trans->setStatus(INCLUDED, mLedgerSeq);
updateAccountState(fromAccount);
updateAccountState(toAccount);
addTransaction(trans);
return TR_SUCCESS;
}
catch (SHAMapException)
{
return TR_ERROR;
}
}
Ledger::TransResult Ledger::removeTransaction(Transaction::pointer trans)
{ // high-level - reverse application of transaction
assert(!mAccepted);
boost::recursive_mutex::scoped_lock sl(mLock);
if(!mTransactionMap || !mAccountStateMap) return TR_ERROR;
try
{
Transaction::pointer ourTrans=getTransaction(trans->getID());
if(!ourTrans) return TR_NOTFOUND;
// accounts exist
AccountState::pointer fromAccount=getAccountState(trans->getFromAccount());
AccountState::pointer toAccount=getAccountState(trans->getToAccount());
if(!fromAccount || !toAccount) return TR_BADACCT;
// pass sanity checks?
if(toAccount->getBalance()<trans->getAmount()) return TR_INSUFF;
if(fromAccount->getSeq()!=(trans->getFromAccountSeq()+1)) return TR_PASTASEQ;
// reverse
fromAccount->credit(trans->getAmount());
fromAccount->decSeq();
toAccount->charge(trans->getAmount()-trans->getFee());
mFeeHeld-=trans->getFee();
trans->setStatus(REMOVED, mLedgerSeq);
if(!delTransaction(trans->getID()))
{
assert(false);
return TR_ERROR;
}
updateAccountState(fromAccount);
updateAccountState(toAccount);
return TR_SUCCESS;
}
catch (SHAMapException)
{
return TR_ERROR;
}
}
Ledger::TransResult Ledger::hasTransaction(Transaction::pointer trans)
{ // Is this transaction in this ledger? If not, could it go in it?
boost::recursive_mutex::scoped_lock sl(mLock);
if(mTransactionMap==NULL) return TR_ERROR;
try
{
Transaction::pointer t=getTransaction(trans->getID());
if(!!t) return TR_ALREADY;
if(trans->getSourceLedger()>mLedgerSeq) return TR_BADLSEQ;
AccountState::pointer fromAccount=getAccountState(trans->getFromAccount());
if(!fromAccount) return TR_BADACCT; // cannot send from non-existent account
// may be in a previous ledger
if(fromAccount->getSeq()>trans->getFromAccountSeq()) return TR_PASTASEQ;
if(fromAccount->getSeq()<trans->getFromAccountSeq()) return TR_PREASEQ;
if(fromAccount->getBalance()<trans->getAmount()) return TR_INSUFF;
return TR_NOTFOUND;
}
catch (SHAMapException)
{
return TR_ERROR;
}
}
Ledger::pointer Ledger::closeLedger(uint64 timeStamp)
{ // close this ledger, return a pointer to the next ledger
// CAUTION: New ledger needs its SHAMap's connected to storage
updateHash();
setClosed();
return Ledger::pointer(new Ledger(*this, timeStamp)); // can't use make_shared
}
void LocalAccount::syncLedger()
{
AccountState::pointer as=theApp->getMasterLedger().getAccountState(getAddress());
if(!as) mLgrBalance=0;
else
{
mLgrBalance=as->getBalance();
if( (mLgrBalance!=0) && (mTxnSeq==0) ) mTxnSeq=1;
if(mTxnSeq<as->getSeq()) mTxnSeq=as->getSeq();
}
}
bool Ledger::unitTest()
{
uint160 la1=theApp->getWallet().addFamily(CKey::PassPhraseToKey("This is my payphrase!"), false);
uint160 la2=theApp->getWallet().addFamily(CKey::PassPhraseToKey("Another payphrase"), false);
LocalAccount::pointer l1=theApp->getWallet().getLocalAccount(la1, 0);
LocalAccount::pointer l2=theApp->getWallet().getLocalAccount(la2, 0);
assert(l1->getAddress()==la1);
#ifdef DEBUG
std::cerr << "Account1: " << la1.GetHex() << std::endl;
std::cerr << "Account2: " << la2.GetHex() << std::endl;
#endif
Ledger::pointer ledger=boost::make_shared<Ledger>(la1, 100000);
ledger=Ledger::pointer(new Ledger(*ledger, 0)); // can't use make_shared
AccountState::pointer as=ledger->getAccountState(la1);
assert(as);
assert(as->getBalance()==100000);
assert(as->getSeq()==0);
as=ledger->getAccountState(la2);
assert(!as);
Transaction::pointer t=boost::make_shared<Transaction>(l1, l2->getAddress(), 2500, 0, 1);
assert(!!t->getID());
Ledger::TransResult tr=ledger->applyTransaction(t);
#ifdef DEBUG
std::cerr << "Transaction: " << tr << std::endl;
#endif
assert(tr==TR_SUCCESS);
return true;
}
uint256 Ledger::getHash()
{
if(!mValidHash) updateHash();
return(mHash);
}
void Ledger::saveAcceptedLedger(Ledger::pointer ledger)
{
std::string sql="INSERT INTO Ledgers "
"(LedgerHash,LedgerSeq,PrevHash,FeeHeld,ClosingTime,AccountSetHash,TransSetHash) VALUES ('";
sql.append(ledger->getHash().GetHex());
sql.append("','");
sql.append(boost::lexical_cast<std::string>(ledger->mLedgerSeq));
sql.append("','");
sql.append(ledger->mParentHash.GetHex());
sql.append("','");
sql.append(boost::lexical_cast<std::string>(ledger->mFeeHeld));
sql.append("','");
sql.append(boost::lexical_cast<std::string>(ledger->mTimeStamp));
sql.append("','");
sql.append(ledger->mAccountHash.GetHex());
sql.append("','");
sql.append(ledger->mTransHash.GetHex());
sql.append("');");
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
theApp->getLedgerDB()->getDB()->executeSQL(sql.c_str());
// write out dirty nodes
while(ledger->mTransactionMap->flushDirty(64, TRANSACTION_NODE, ledger->mLedgerSeq))
{ ; }
while(ledger->mAccountStateMap->flushDirty(64, ACCOUNT_NODE, ledger->mLedgerSeq))
{ ; }
}
Ledger::pointer Ledger::getSQL(const std::string& sql)
{
uint256 ledgerHash, prevHash, accountHash, transHash;
uint64 feeHeld, closingTime;
uint32 ledgerSeq;
std::string hash;
if(1)
{
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
Database *db=theApp->getLedgerDB()->getDB();
if(!db->executeSQL(sql.c_str()) || !db->startIterRows() || !db->getNextRow())
return Ledger::pointer();
db->getStr("LedgerHash", hash);
ledgerHash.SetHex(hash);
db->getStr("PrevHash", hash);
prevHash.SetHex(hash);
db->getStr("AccountSetHash", hash);
accountHash.SetHex(hash);
db->getStr("TransSetHash", hash);
transHash.SetHex(hash);
feeHeld=db->getBigInt("FeeHeld");
closingTime=db->getBigInt("ClosingTime");
ledgerSeq=db->getBigInt("LedgerSeq");
db->endIterRows();
}
Ledger::pointer ret=boost::make_shared<Ledger>(prevHash, transHash, accountHash, feeHeld, closingTime, ledgerSeq);
if(ret->getHash()!=ledgerHash)
{
assert(false);
return Ledger::pointer();
}
return ret;
}
Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex)
{
std::string sql="SELECT * from Ledgers WHERE LedgerSeq='";
sql.append(boost::lexical_cast<std::string>(ledgerIndex));
sql.append("';");
return getSQL(sql);
}
Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash)
{
std::string sql="SELECT * from Ledgers WHERE LedgerHash='";
sql.append(ledgerHash.GetHex());
sql.append("';");
return getSQL(sql);
}
void Ledger::addJson(Json::Value& ret)
{
Json::Value ledger(Json::objectValue);
boost::recursive_mutex::scoped_lock sl(mLock);
ledger["ParentHash"]=mParentHash.GetHex();
if(mClosed)
{
ledger["Hash"]=mHash.GetHex();
ledger["TransactionHash"]=mTransHash.GetHex();
ledger["AccountHash"]=mAccountHash.GetHex();
ledger["Closed"]=true;
ledger["Accepted"]=mAccepted;
}
else ledger["Closed"]=false;
ret[boost::lexical_cast<std::string>(mLedgerSeq)]=ledger;
}
Ledger::pointer Ledger::switchPreviousLedger(Ledger::pointer oldPrevious, Ledger::pointer newPrevious, int limit)
{
// Build a new ledger that can replace this ledger as the active ledger,
// with a different previous ledger. We assume our ledger is trusted, as is its
// previous ledger. We make no assumptions about the new previous ledger.
int count;
// 1) Validate sequences and make sure the specified ledger is a valid prior ledger
if(newPrevious->getLedgerSeq()!=oldPrevious->getLedgerSeq()) return Ledger::pointer();
// 2) Begin building a new ledger with the specified ledger as previous.
Ledger* newLedger=new Ledger(*newPrevious, mTimeStamp);
// 3) For any transactions in our previous ledger but not in the new previous ledger, add them to the set
SHAMap::SHAMapDiff mapDifferences;
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> > TxnDiff;
if(!newPrevious->mTransactionMap->compare(oldPrevious->mTransactionMap, mapDifferences, limit))
return Ledger::pointer();
if(!Transaction::convertToTransactions(oldPrevious->getLedgerSeq(), newPrevious->getLedgerSeq(),
false, true, mapDifferences, TxnDiff))
return Ledger::pointer(); // new previous ledger contains invalid transactions
// 4) Try to add those transactions to the new ledger.
do
{
count=0;
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >::iterator it=TxnDiff.begin();
while(it!=TxnDiff.end())
{
Transaction::pointer& tx=it->second.second;
if(!tx || newLedger->addTransaction(tx))
{
count++;
TxnDiff.erase(it++);
}
else ++it;
}
} while(count!=0);
// WRITEME: Handle rejected transactions left in TxnDiff
// 5) Try to add transactions from this ledger to the new ledger.
std::map<uint256, Transaction::pointer> txnMap;
for(SHAMapItem::pointer mit=peekTransactionMap()->peekFirstItem();
!!mit;
mit=peekTransactionMap()->peekNextItem(mit->getTag()))
{
uint256 txnID=mit->getTag();
Transaction::pointer tx=theApp->getMasterTransaction().fetch(txnID, false);
if(!tx) tx=boost::make_shared<Transaction>(mit->peekData(), false);
txnMap.insert(std::make_pair(txnID, tx));
}
do
{
count=0;
std::map<uint256, Transaction::pointer>::iterator it=txnMap.begin();
while(it!=txnMap.end())
{
if(newLedger->addTransaction(it->second))
{
count++;
txnMap.erase(it++);
}
else ++it;
}
} while(count!=0);
// WRITEME: Handle rejected transactions left in txnMap
return Ledger::pointer(newLedger);
}
void Ledger::setAcquiring(void)
{
if(!mTransactionMap || !mAccountStateMap) throw SHAMapException(InvalidMap);
mTransactionMap->setSynching();
mAccountStateMap->setSynching();
}
bool Ledger::isAcquiring(void)
{
return isAcquiringTx() || isAcquiringAS();
}
bool Ledger::isAcquiringTx(void)
{
return mTransactionMap->isSynching();
}
bool Ledger::isAcquiringAS(void)
{
return mAccountStateMap->isSynching();
}

124
src/Ledger.h Normal file
View File

@@ -0,0 +1,124 @@
#ifndef __LEDGER__
#define __LEDGER__
#include <map>
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "../json/value.h"
#include "Transaction.h"
#include "types.h"
#include "BitcoinUtil.h"
#include "Hanko.h"
#include "AccountState.h"
#include "SHAMap.h"
class Ledger : public boost::enable_shared_from_this<Ledger>
{ // The basic Ledger structure, can be opened, closed, or synching
public:
typedef boost::shared_ptr<Ledger> pointer;
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
};
private:
uint256 mHash, mParentHash, mTransHash, mAccountHash;
uint64 mFeeHeld, mTimeStamp;
uint32 mLedgerSeq;
bool mClosed, mValidHash, mAccepted, mImmutable;
SHAMap::pointer mTransactionMap, mAccountStateMap;
mutable boost::recursive_mutex mLock;
Ledger(const Ledger&); // no implementation
Ledger& operator=(const Ledger&); // no implementation
protected:
Ledger(Ledger& previous, uint64 timestamp); // ledger after this one
void updateHash();
bool addAccountState(AccountState::pointer);
bool updateAccountState(AccountState::pointer);
bool addTransaction(Transaction::pointer);
bool delTransaction(const uint256& id);
static Ledger::pointer getSQL(const std::string& sqlStatement);
public:
Ledger(const uint160& masterID, uint64 startAmount); // used for the starting bootstrap ledger
Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
uint64 feeHeld, uint64 timeStamp, uint32 ledgerSeq); // used for received ledgers
Ledger(const std::vector<unsigned char>& rawLedger);
Ledger(const std::string& rawLedger);
void setClosed() { mClosed=true; }
void setAccepted() { mAccepted=true; }
bool isClosed() { return mClosed; }
bool isAccepted() { return mAccepted; }
// ledger signature operations
void addRaw(Serializer &s);
uint256 getHash();
const uint256& getParentHash() const { return mParentHash; }
const uint256& getTransHash() const { return mTransHash; }
const uint256& getAccountHash() const { return mAccountHash; }
uint64 getFeeHeld() const { return mFeeHeld; }
uint64 getTimeStamp() const { return mTimeStamp; }
uint32 getLedgerSeq() const { return mLedgerSeq; }
// low level functions
SHAMap::pointer peekTransactionMap() { return mTransactionMap; }
SHAMap::pointer peekAccountStateMap() { return mAccountStateMap; }
// ledger sync functions
void setAcquiring(void);
bool isAcquiring(void);
bool isAcquiringTx(void);
bool isAcquiringAS(void);
// mid level functions
bool hasTransaction(const uint256& TransID) const;
AccountState::pointer getAccountState(const uint160& acctID);
Transaction::pointer getTransaction(const uint256& transID) const;
uint64 getBalance(const uint160& acctID) const;
// high level functions
TransResult applyTransaction(Transaction::pointer trans);
TransResult removeTransaction(Transaction::pointer trans);
TransResult hasTransaction(Transaction::pointer trans);
Ledger::pointer switchPreviousLedger(Ledger::pointer oldPrevious, Ledger::pointer newPrevious, int limit);
// database functions
static void saveAcceptedLedger(Ledger::pointer);
static Ledger::pointer loadByIndex(uint32 ledgerIndex);
static Ledger::pointer loadByHash(const uint256& ledgerHash);
Ledger::pointer closeLedger(uint64 timestamp);
bool isCompatible(boost::shared_ptr<Ledger> other);
bool signLedger(std::vector<unsigned char> &signature, const LocalHanko &hanko);
void addJson(Json::Value&);
static bool unitTest();
};
#endif

308
src/LedgerAcquire.cpp Normal file
View File

@@ -0,0 +1,308 @@
#include "boost/foreach.hpp"
#include "boost/make_shared.hpp"
#include "Application.h"
#include "LedgerAcquire.h"
LedgerAcquire::LedgerAcquire(const uint256& hash) : mHash(hash),
mComplete(false), mFailed(false), mHaveBase(false), mHaveState(false), mHaveTransactions(false)
{
;
}
void LedgerAcquire::done()
{
std::vector< boost::function<void (LedgerAcquire::pointer)> > triggers;
mLock.lock();
triggers=mOnComplete;
mOnComplete.empty();
mLock.unlock();
for(int i=0; i<triggers.size(); i++)
triggers[i](shared_from_this());
}
void LedgerAcquire::setTimer()
{
// WRITEME
}
void LedgerAcquire::timerEntry(boost::weak_ptr<LedgerAcquire> wptr)
{
LedgerAcquire::pointer ptr=wptr.lock();
if(ptr) ptr->trigger(true);
}
void LedgerAcquire::addOnComplete(boost::function<void (LedgerAcquire::pointer)> trigger)
{
mLock.lock();
mOnComplete.push_back(trigger);
mLock.unlock();
}
void LedgerAcquire::trigger(bool timer)
{
if(mComplete || mFailed) return;
if(!mHaveBase)
{
// WRITEME: Do we need to search for peers?
boost::shared_ptr<newcoin::TMGetLedger> tmGL=boost::make_shared<newcoin::TMGetLedger>();
tmGL->set_ledgerhash(mHash.begin(), mHash.size());
tmGL->set_itype(newcoin::liBASE);
sendRequest(tmGL);
}
if(mHaveBase && !mHaveTransactions)
{
assert(mLedger);
if(mLedger->peekTransactionMap()->getHash().isZero())
{ // we need the root node
boost::shared_ptr<newcoin::TMGetLedger> tmGL=boost::make_shared<newcoin::TMGetLedger>();
tmGL->set_ledgerhash(mHash.begin(), mHash.size());
tmGL->set_ledgerseq(mLedger->getLedgerSeq());
tmGL->set_itype(newcoin::liTX_NODE);
*(tmGL->add_nodeids())=SHAMapNode().getRawString();
sendRequest(tmGL);
}
else
{
std::vector<SHAMapNode> nodeIDs;
std::vector<uint256> nodeHashes;
mLedger->peekTransactionMap()->getMissingNodes(nodeIDs, nodeHashes, 128);
if(nodeIDs.empty())
{
if(!mLedger->peekTransactionMap()->isValid()) mFailed=true;
else
{
mHaveTransactions=true;
if(mHaveState) mComplete=true;
}
}
else
{
boost::shared_ptr<newcoin::TMGetLedger> tmGL=boost::make_shared<newcoin::TMGetLedger>();
tmGL->set_ledgerhash(mHash.begin(), mHash.size());
tmGL->set_ledgerseq(mLedger->getLedgerSeq());
tmGL->set_itype(newcoin::liTX_NODE);
for(std::vector<SHAMapNode>::iterator it=nodeIDs.begin(); it!=nodeIDs.end(); ++it)
*(tmGL->add_nodeids())=it->getRawString();
sendRequest(tmGL);
}
}
}
if(mHaveBase && !mHaveState)
{
assert(mLedger);
if(mLedger->peekAccountStateMap()->getHash().isZero())
{ // we need the root node
boost::shared_ptr<newcoin::TMGetLedger> tmGL=boost::make_shared<newcoin::TMGetLedger>();
tmGL->set_ledgerhash(mHash.begin(), mHash.size());
tmGL->set_ledgerseq(mLedger->getLedgerSeq());
tmGL->set_itype(newcoin::liAS_NODE);
*(tmGL->add_nodeids())=SHAMapNode().getRawString();
sendRequest(tmGL);
}
else
{
std::vector<SHAMapNode> nodeIDs;
std::vector<uint256> nodeHashes;
mLedger->peekAccountStateMap()->getMissingNodes(nodeIDs, nodeHashes, 128);
if(nodeIDs.empty())
{
if(!mLedger->peekAccountStateMap()->isValid()) mFailed=true;
else
{
mHaveState=true;
if(mHaveTransactions) mComplete=true;
}
}
else
{
boost::shared_ptr<newcoin::TMGetLedger> tmGL=boost::make_shared<newcoin::TMGetLedger>();
tmGL->set_ledgerhash(mHash.begin(), mHash.size());
tmGL->set_ledgerseq(mLedger->getLedgerSeq());
tmGL->set_itype(newcoin::liAS_NODE);
for(std::vector<SHAMapNode>::iterator it=nodeIDs.begin(); it!=nodeIDs.end(); ++it)
*(tmGL->add_nodeids())=it->getRawString();
sendRequest(tmGL);
}
}
}
if(mComplete || mFailed)
done();
else if(timer)
setTimer();
}
void LedgerAcquire::sendRequest(boost::shared_ptr<newcoin::TMGetLedger> tmGL)
{
boost::recursive_mutex::scoped_lock sl(mLock);
if(mPeers.empty()) return;
PackedMessage::pointer packet=boost::make_shared<PackedMessage>(tmGL, newcoin::mtGET_LEDGER);
std::list<boost::weak_ptr<Peer> >::iterator it=mPeers.begin();
while(it!=mPeers.end())
{
if(it->expired())
mPeers.erase(it++);
else
{
// FIXME: Track last peer sent to and time sent
it->lock()->sendPacket(packet);
return;
}
}
}
void LedgerAcquire::peerHas(Peer::pointer ptr)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::list<boost::weak_ptr<Peer> >::iterator it=mPeers.begin();
while(it!=mPeers.end())
{
Peer::pointer pr=it->lock();
if(!pr) // we have a dead entry, remove it
it=mPeers.erase(it);
else
{
if(pr->samePeer(ptr)) return; // we already have this peer
++it;
}
}
mPeers.push_back(ptr);
}
void LedgerAcquire::badPeer(Peer::pointer ptr)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::list<boost::weak_ptr<Peer> >::iterator it=mPeers.begin();
while(it!=mPeers.end())
{
Peer::pointer pr=it->lock();
if(!pr) // we have a dead entry, remove it
it=mPeers.erase(it);
else
{
if(ptr->samePeer(pr))
{ // We found a pointer to the bad peer
mPeers.erase(it);
return;
}
++it;
}
}
}
bool LedgerAcquire::takeBase(const std::string& data)
{ // Return value: true=normal, false=bad data
boost::recursive_mutex::scoped_lock sl(mLock);
if(mHaveBase) return true;
Ledger* ledger=new Ledger(data);
if(ledger->getHash()!=mHash)
{
delete ledger;
return false;
}
mLedger=Ledger::pointer(ledger);
mLedger->setAcquiring();
mHaveBase=true;
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();
while(nodeIDit!=nodeIDs.end())
{
if(!mLedger->peekTransactionMap()->addKnownNode(*nodeIDit, *nodeDatait))
return false;
++nodeIDit;
++nodeDatait;
}
if(!mLedger->peekTransactionMap()->isSynching()) mHaveTransactions=true;
return true;
}
bool LedgerAcquire::takeAsNode(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();
while(nodeIDit!=nodeIDs.end())
{
if(!mLedger->peekAccountStateMap()->addKnownNode(*nodeIDit, *nodeDatait))
return false;
++nodeIDit;
++nodeDatait;
}
if(!mLedger->peekAccountStateMap()->isSynching()) mHaveState=true;
return true;
}
LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash)
{
boost::mutex::scoped_lock sl(mLock);
LedgerAcquire::pointer& ptr=mLedgers[hash];
if(ptr) return ptr;
return boost::make_shared<LedgerAcquire>(hash);
}
LedgerAcquire::pointer LedgerAcquireMaster::find(const uint256& hash)
{
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)
{
boost::mutex::scoped_lock sl(mLock);
return mLedgers.find(hash)!=mLedgers.end();
}
bool LedgerAcquireMaster::gotLedgerData(newcoin::TMLedgerData& packet)
{
uint256 hash;
if(packet.ledgerhash().size()!=32) return false;
memcpy(&hash, packet.ledgerhash().data(), 32);
LedgerAcquire::pointer ledger=find(hash);
if(!ledger) return false;
if(packet.type()==newcoin::liBASE)
{
if(packet.nodes_size()!=1) return false;
const newcoin::TMLedgerNode& node=packet.nodes(0);
if(!node.has_nodedata()) return false;
return ledger->takeBase(node.nodedata());
}
else if( (packet.type()==newcoin::liTX_NODE) || (packet.type()==newcoin::liAS_NODE) )
{
std::list<SHAMapNode> nodeIDs;
std::list<std::vector<unsigned char> > nodeData;
if(packet.nodes().size()<=0) return false;
for(int i=0; i<packet.nodes().size(); i++)
{
const newcoin::TMLedgerNode& node=packet.nodes(i);
if(!node.has_nodeid() || !node.has_nodedata()) 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()));
}
if(packet.type()==newcoin::liTX_NODE) return ledger->takeTxNode(nodeIDs, nodeData);
else return ledger->takeAsNode(nodeIDs, nodeData);
}
else return false;
}

70
src/LedgerAcquire.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef __LEDGERACQUIRE__
#define __LEDGERACQUIRE__
#include <vector>
#include <map>
#include "boost/enable_shared_from_this.hpp"
#include "boost/function.hpp"
#include "Ledger.h"
#include "Peer.h"
#include "../obj/src/newcoin.pb.h"
class LedgerAcquire : public boost::enable_shared_from_this<LedgerAcquire>
{ // A ledger we are trying to acquire
public:
typedef boost::shared_ptr<LedgerAcquire> pointer;
protected:
boost::recursive_mutex mLock;
Ledger::pointer mLedger;
uint256 mHash;
bool mComplete, mFailed, mHaveBase, mHaveState, mHaveTransactions;
std::vector< boost::function<void (LedgerAcquire::pointer)> > mOnComplete;
std::list<boost::weak_ptr<Peer> > mPeers; // peers known to have this ledger
void done();
void trigger(bool timer);
static void timerEntry(boost::weak_ptr<LedgerAcquire>);
void sendRequest(boost::shared_ptr<newcoin::TMGetLedger> message);
void setTimer();
public:
LedgerAcquire(const uint256& hash);
const uint256& getHash() const { return mHash; }
bool isComplete() const { return mComplete; }
bool isFailed() const { return mFailed; }
bool isBase() const { return mHaveBase; }
bool isAcctStComplete() const { return mHaveState; }
bool isTransComplete() const { return mHaveTransactions; }
Ledger::pointer getLedger() { return mLedger; }
void addOnComplete(boost::function<void (LedgerAcquire::pointer)>);
void peerHas(Peer::pointer);
void badPeer(Peer::pointer);
bool takeBase(const std::string& data);
bool takeTxNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data);
bool takeAsNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data);
};
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);
bool gotLedgerData(newcoin::TMLedgerData& packet);
};
#endif

86
src/LedgerHistory.cpp Normal file
View File

@@ -0,0 +1,86 @@
#include <string>
#include "boost/bind.hpp"
#include "LedgerHistory.h"
#include "Config.h"
#include "Application.h"
#ifndef CACHED_LEDGER_NUM
#define CACHED_LEDGER_NUM 512
#endif
#ifndef CACHED_LEDGER_AGE
#define CACHED_LEDGER_AGE 60
#endif
LedgerHistory::LedgerHistory() : mLedgersByHash(CACHED_LEDGER_NUM, CACHED_LEDGER_AGE)
{
;
}
void LedgerHistory::addLedger(Ledger::pointer ledger)
{
mLedgersByHash.canonicalize(ledger->getHash(), ledger);
}
void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger)
{
assert(ledger && ledger->isAccepted());
uint256 h(ledger->getHash());
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
mLedgersByHash.canonicalize(h, ledger);
mLedgersByIndex.insert(std::make_pair(ledger->getLedgerSeq(), ledger));
theApp->getIOService().post(boost::bind(&Ledger::saveAcceptedLedger, ledger));
}
Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index)
{
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
std::map<uint32, Ledger::pointer>::iterator it(mLedgersByIndex.find(index));
if(it!=mLedgersByIndex.end()) return it->second;
sl.unlock();
Ledger::pointer ret(Ledger::loadByIndex(index));
if(!ret) return ret;
assert(ret->getLedgerSeq()==index);
sl.lock();
mLedgersByHash.canonicalize(ret->getHash(), ret);
mLedgersByIndex.insert(std::make_pair(index, ret));
return ret;
}
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);
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
mLedgersByHash.canonicalize(hash, ret);
if(ret->isAccepted()) mLedgersByIndex[ret->getLedgerSeq()]=ret;
return ret;
}
Ledger::pointer LedgerHistory::canonicalizeLedger(Ledger::pointer ledger, bool save)
{
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;
return ledger;
}

23
src/LedgerHistory.h Normal file
View File

@@ -0,0 +1,23 @@
#ifndef __LEDGERHISTORY__
#define __LEDGERHISTORY__
#include "TaggedCache.h"
#include "Ledger.h"
class LedgerHistory
{
TaggedCache<uint256, Ledger> mLedgersByHash;
std::map<uint32, Ledger::pointer> mLedgersByIndex; // accepted ledgers
public:
LedgerHistory();
void addLedger(Ledger::pointer ledger);
void addAcceptedLedger(Ledger::pointer ledger);
Ledger::pointer getLedgerBySeq(uint32 index);
Ledger::pointer getLedgerByHash(const uint256& hash);
Ledger::pointer canonicalizeLedger(Ledger::pointer, bool cache);
};
#endif

196
src/LedgerMaster.cpp Normal file
View File

@@ -0,0 +1,196 @@
#include "LedgerMaster.h"
#include "Application.h"
#include "NewcoinAddress.h"
#include "TimingService.h"
#include "Conversion.h"
#include <boost/foreach.hpp>
LedgerMaster::LedgerMaster() : mIsSynced(false)
{
}
uint32 LedgerMaster::getCurrentLedgerIndex()
{
return mCurrentLedger->getLedgerSeq();
}
uint64 LedgerMaster::getBalance(const uint160& addr)
{
return mCurrentLedger->getBalance(addr);
}
uint64 LedgerMaster::getBalance(std::string& addr)
{
return mCurrentLedger->getBalance(humanTo160(addr));
}
bool LedgerMaster::addHeldTransaction(Transaction::pointer transaction)
{ // returns true if transaction was added
boost::recursive_mutex::scoped_lock ml(mLock);
return mHeldTransactionsByID.insert(std::make_pair(transaction->getID(), transaction)).second;
}
void LedgerMaster::pushLedger(Ledger::pointer newLedger)
{
ScopedLock sl(mLock);
if(!!mFinalizingLedger)
{
mFinalizingLedger->setClosed();
mFinalizingLedger->setAccepted();
mLedgerHistory.addAcceptedLedger(mFinalizingLedger);
}
mFinalizingLedger=mCurrentLedger;
mCurrentLedger=newLedger;
}
#if 0
void LedgerMaster::startFinalization()
{
mFinalizingLedger=mCurrentLedger;
mCurrentLedger=Ledger::pointer(new Ledger(mCurrentLedger->getIndex()+1));
applyFutureProposals( mFinalizingLedger->getIndex() );
applyFutureTransactions( mCurrentLedger->getIndex() );
}
void LedgerMaster::sendProposal()
{
PackedMessage::pointer packet=Peer::createLedgerProposal(mFinalizingLedger);
theApp->getConnectionPool().relayMessage(NULL,packet);
}
void LedgerMaster::endFinalization()
{
mFinalizingLedger->publishValidation();
mLedgerHistory.addAcceptedLedger(mFinalizingLedger);
mLedgerHistory.addLedger(mFinalizingLedger);
mFinalizingLedger=Ledger::pointer();
}
void LedgerMaster::addFutureProposal(Peer::pointer peer,newcoin::ProposeLedger& otherLedger)
{
mFutureProposals.push_front(pair<Peer::pointer,newcoin::ProposeLedger>(peer,otherLedger));
}
void LedgerMaster::applyFutureProposals(uint32 ledgerIndex)
{
for(list< pair<Peer::pointer,newcoin::ProposeLedger> >::iterator iter=mFutureProposals.begin(); iter !=mFutureProposals.end(); )
{
if( (*iter).second.ledgerindex() == ledgerIndex)
{
checkLedgerProposal((*iter).first,(*iter).second);
mFutureProposals.erase(iter);
}else iter++;
}
}
void LedgerMaster::applyFutureTransactions(uint32 ledgerIndex)
{
for(list<TransactionPtr>::iterator iter=mFutureTransactions.begin(); iter !=mFutureTransactions.end(); )
{
if( (*iter)->ledgerindex() == ledgerIndex)
{
addTransaction(*iter);
mFutureTransactions.erase(iter);
}else iter++;
}
}
void LedgerMaster::checkLedgerProposal(Peer::pointer peer, newcoin::ProposeLedger& otherLedger)
{
// see if this matches yours
// if you haven't finalized yet save it for when you do
// if doesn't match and you have <= transactions ask for the complete ledger
// if doesn't match and you have > transactions send your complete ledger
if(otherLedger.ledgerindex()<mCurrentLedger->getIndex())
{
if( (!mFinalizingLedger) ||
otherLedger.ledgerindex()<mFinalizingLedger->getIndex())
{ // you have already closed this ledger
Ledger::pointer oldLedger=mLedgerHistory.getAcceptedLedger(otherLedger.ledgerindex());
if(oldLedger)
{
if( (oldLedger->getHash()!=protobufTo256(otherLedger.hash())) &&
(oldLedger->getNumTransactions()>=otherLedger.numtransactions()))
{
peer->sendLedgerProposal(oldLedger);
}
}
}else
{ // you guys are on the same page
uint256 otherHash=protobufTo256(otherLedger.hash());
if(mFinalizingLedger->getHash()!= otherHash)
{
if( mFinalizingLedger->getNumTransactions()>=otherLedger.numtransactions())
{
peer->sendLedgerProposal(mFinalizingLedger);
}else
{
peer->sendGetFullLedger(otherHash);
}
}
}
}else
{ // you haven't started finalizing this one yet save it for when you do
addFutureProposal(peer,otherLedger);
}
}
// TODO: optimize. this is expensive so limit the amount it is run
void LedgerMaster::checkConsensus(uint32 ledgerIndex)
{
Ledger::pointer ourAcceptedLedger=mLedgerHistory.getAcceptedLedger(ledgerIndex);
if(ourAcceptedLedger)
{
Ledger::pointer consensusLedger;
uint256 consensusHash;
if( theApp->getValidationCollection().getConsensusLedger(ledgerIndex,ourAcceptedLedger->getHash(), consensusLedger, consensusHash) )
{ // our accepted ledger isn't compatible with the consensus
if(consensusLedger)
{ // switch to this ledger. Re-validate
mLedgerHistory.addAcceptedLedger(consensusLedger);
consensusLedger->publishValidation();
}else
{ // we don't know the consensus one. Ask peers for it
// TODO: make sure this isn't sent many times before we have a chance to get a reply
PackedMessage::pointer msg=Peer::createGetFullLedger(consensusHash);
theApp->getConnectionPool().relayMessage(NULL,msg);
}
}
}
}
/*
if( consensusHash &&
(ourAcceptedLedger->getHash()!= *consensusHash))
{
Ledger::pointer consensusLedger=mLedgerHistory.getLedger(*consensusHash);
if(consensusLedger)
{ // see if these are compatible
if(ourAcceptedLedger->isCompatible(consensusLedger))
{ // try to merge any transactions from the consensus one into ours
ourAcceptedLedger->mergeIn(consensusLedger);
// Ledger::pointer child=ourAcceptedLedger->getChild();
Ledger::pointer child=mLedgerHistory.getAcceptedLedger(ledgerIndex+1);
if(child) child->recalculate();
}else
{ // switch to this ledger. Re-validate
mLedgerHistory.addAcceptedLedger(consensusLedger);
consensusLedger->publishValidation();
}
}else
{ // we don't know the consensus one. Ask peers for it
PackedMessage::pointer msg=Peer::createGetFullLedger(*consensusHash);
theApp->getConnectionPool().relayMessage(NULL,msg);
}
}
*/
#endif

65
src/LedgerMaster.h Normal file
View File

@@ -0,0 +1,65 @@
#ifndef __LEDGERMASTER__
#define __LEDGERMASTER__
#include "Ledger.h"
#include "LedgerHistory.h"
#include "Peer.h"
#include "types.h"
#include "Transaction.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;
bool mIsSynced;
Ledger::pointer mCurrentLedger;
Ledger::pointer mFinalizingLedger;
LedgerHistory mLedgerHistory;
std::map<uint256, Transaction::pointer> mHeldTransactionsByID;
void applyFutureTransactions(uint32 ledgerIndex);
bool isValidTransaction(Transaction::pointer trans);
bool isTransactionOnFutureList(Transaction::pointer trans);
public:
LedgerMaster();
uint32 getCurrentLedgerIndex();
bool isSynced() { return mIsSynced; }
void setSynced() { mIsSynced=true; }
Ledger::pointer getCurrentLedger() { return mCurrentLedger; }
Ledger::pointer getClosingLedger() { return mFinalizingLedger; }
void pushLedger(Ledger::pointer newLedger);
Ledger::pointer getLedgerBySeq(uint32 index)
{
if(mCurrentLedger && (mCurrentLedger->getLedgerSeq()==index)) return mCurrentLedger;
if(mFinalizingLedger && (mFinalizingLedger->getLedgerSeq()==index)) return mFinalizingLedger;
return mLedgerHistory.getLedgerBySeq(index);
}
Ledger::pointer getLedgerByHash(const uint256& hash)
{
if(mCurrentLedger && (mCurrentLedger->getHash()==hash)) return mCurrentLedger;
if(mFinalizingLedger && (mFinalizingLedger->getHash()==hash)) return mFinalizingLedger;
return mLedgerHistory.getLedgerByHash(hash);
}
uint64 getBalance(std::string& addr);
uint64 getBalance(const uint160& addr);
AccountState::pointer getAccountState(const uint160& addr)
{ return mCurrentLedger->getAccountState(addr); }
bool addHeldTransaction(Transaction::pointer trans);
};
#endif

115
src/LocalAccount.h Normal file
View File

@@ -0,0 +1,115 @@
#ifndef __LOCALACCOUNT__
#define __LOCALACCOUNT__
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
class LocalAccountFamily;
class LocalAccount
{ // tracks keys for local accounts
public:
typedef boost::shared_ptr<LocalAccount> pointer;
protected:
// core account information
CKey::pointer mPublicKey;
uint160 mAcctID;
std::string mName, mComment;
// family information
boost::shared_ptr<LocalAccountFamily> mFamily;
int mAccountFSeq;
// local usage tracking
uint64 mLgrBalance; // The balance, from the last ledger
int64 mTxnDelta; // The balance changes from local/pending transactions
uint32 mTxnSeq; // The sequence number of the next transaction
public:
LocalAccount(boost::shared_ptr<LocalAccountFamily> family, int accountSeq);
// Database operations
bool read(); // reads any existing data
bool write(); // creates the record in the first place
bool updateName(); // writes changed name/comment
bool updateBalance(); // writes changed balance/seq
const uint160& getAddress() const { return mAcctID; }
int getAcctFSeq() const { return mAccountFSeq; }
std::string getLocalAccountName() const; // The name used locally to identify this account
std::string getAccountName() const; // The normal account name used to send to this account
std::string getFullName() const;
std::string getShortName() const;
std::string getFamilyName() const;
bool isLocked() const;
bool isIssued() const;
CKey::pointer getPublicKey() { return mPublicKey; }
CKey::pointer getPrivateKey();
Json::Value getJson() const;
void update(uint64 balance, uint32 seq);
uint32 getTxnSeq() const { return mTxnSeq; }
uint32 incTxnSeq() { return mTxnSeq++; }
int64 getEffectiveBalance() const { return static_cast<int64_t>(mLgrBalance)+mTxnDelta; }
void credit(uint64 amount) { mTxnDelta+=amount; }
void debit(uint64 amount) { mTxnDelta-=amount; }
void setLedgerBalance(uint64_t lb) { mLgrBalance=lb; if(mTxnSeq==0) mTxnSeq=1; }
void syncLedger();
};
class LocalAccountFamily : public boost::enable_shared_from_this<LocalAccountFamily>
{ // tracks families of local accounts
public:
typedef boost::shared_ptr<LocalAccountFamily> pointer;
protected:
std::map<int, LocalAccount::pointer> mAccounts;
uint160 mFamily; // the name for this account family
EC_POINT* mRootPubKey;
uint32 mLastSeq;
std::string mName, mComment;
BIGNUM* mRootPrivateKey;
public:
LocalAccountFamily(const uint160& family, const EC_GROUP* group, const EC_POINT* pubKey);
~LocalAccountFamily();
const uint160& getFamily() const { return mFamily; }
void unlock(const BIGNUM* privateKey);
void lock();
bool isLocked() const { return mRootPrivateKey==NULL; }
void setSeq(uint32 s) { mLastSeq=s; }
uint32 getSeq() { return mLastSeq; }
void setName(const std::string& n) { mName=n; }
void setComment(const std::string& c) { mComment=c; }
std::map<int, LocalAccount::pointer>& getAcctMap() { return mAccounts; }
LocalAccount::pointer get(int seq);
uint160 getAccount(int seq, bool keep);
CKey::pointer getPrivateKey(int seq);
CKey::pointer getPublicKey(int seq);
std::string getPubGenHex() const; // The text name of the public key
std::string getShortName() const { return mName; }
std::string getComment() const { return mComment; }
Json::Value getJson() const;
static std::string getSQLFields();
std::string getSQL() const;
static LocalAccountFamily::pointer readFamily(const uint160& family);
void write(bool is_new);
};
#endif

56
src/LocalTransaction.cpp Normal file
View File

@@ -0,0 +1,56 @@
#include <boost/lexical_cast.hpp>
#include "../json/writer.h"
#include "LocalTransaction.h"
#include "Application.h"
#include "Wallet.h"
bool LocalTransaction::makeTransaction()
{
if(!!mTransaction) return true;
LocalAccount::pointer lac(theApp->getWallet().findAccountForTransaction(mAmount));
if(!lac)
{
std::cerr << "Account with sufficient balance not found" << std::endl;
return false;
}
mTransaction=Transaction::pointer(new Transaction(lac, mDestAcctID, mAmount, mTag,
theApp->getOPs().getCurrentLedgerID()));
if(mTransaction->getStatus()!=NEW)
{
#ifdef DEBUG
std::cerr << "Status not NEW" << std::endl;
Json::Value t=mTransaction->getJson(true);
Json::StyledStreamWriter w;
w.write(std::cerr, t);
#endif
return false;
}
return true;
}
void LocalTransaction::performTransaction()
{
mTransaction=theApp->getOPs().processTransaction(mTransaction);
}
Json::Value LocalTransaction::getJson() const
{
if(!mTransaction)
{ // has no corresponding transaction
Json::Value ret(Json::objectValue);
ret["Status"]="unfunded";
ret["Amount"]=boost::lexical_cast<std::string>(mAmount);
Json::Value destination(Json::objectValue);
destination["AccountID"]=NewcoinAddress(mDestAcctID).GetString();
ret["Destination"]=destination;
return ret;
}
return mTransaction->getJson(true, isPaid(), isCredited());
}

58
src/LocalTransaction.h Normal file
View File

@@ -0,0 +1,58 @@
#ifndef __LOCALTRANSACTION__
#define __LOCALTRANSACTION__
// A structure to represent a local transaction
#include <string>
#include <boost/shared_ptr.hpp>
#include "../json/value.h"
#include "uint256.h"
#include "Transaction.h"
class LocalTransaction
{
public:
typedef boost::shared_ptr<LocalTransaction> pointer;
protected:
// core specifications
uint160 mDestAcctID;
uint64 mAmount;
uint32 mTag;
std::string mComment;
bool mPaid, mCredited;
Transaction::pointer mTransaction;
public:
LocalTransaction(const uint160 &dest, uint64 amount, uint32 tag) :
mDestAcctID(dest), mAmount(amount), mTag(tag), mPaid(false), mCredited(false) { ; }
void setComment(const std::string& comment) { mComment=comment; }
const uint160& getDestinationAccount() const { return mDestAcctID; }
uint64 getAmount() const { return mAmount; }
uint32 getTag() const { return mTag; }
const std::string& getComment() const { return mComment; }
Transaction::pointer getTransaction() { return mTransaction; }
void setTransaction(Transaction::pointer t) { mTransaction=t; }
bool isPaid() const { return mPaid; }
void setPaid() { mPaid=true; }
void setUnpaid() { mPaid=false; }
bool isCredited() const { return mCredited; }
void setCredited() { mCredited=true; }
void setUncredited() { mCredited=false; }
void performTransaction(); // perform this transaction as if we received it from the network
bool makeTransaction(); // create a transaction object according to these rules
Json::Value getJson() const;
};
#endif

131
src/NetworkOPs.cpp Normal file
View File

@@ -0,0 +1,131 @@
#include "Application.h"
#include "NetworkOPs.h"
#include "Transaction.h"
// This is the primary interface into the "client" portion of the program.
// Code that wants to do normal operations on the network such as
// creating and monitoring accounts, creating transactions, and so on
// should use this interface. The RPC code will primarily be a light wrapper
// over this code.
// Eventually, it will check the node's operating mode (synched, unsynched,
// etectera) and defer to the correct means of processing. The current
// code assumes this node is synched (and will continue to do so until
// there's a functional network.
uint64 NetworkOPs::getNetworkTime()
{
return time(NULL);
}
uint32 NetworkOPs::getCurrentLedgerID()
{
return theApp->getMasterLedger().getCurrentLedger()->getLedgerSeq();
}
Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, Peer* source)
{
Transaction::pointer dbtx=theApp->getMasterTransaction().fetch(trans->getID(), true);
if(dbtx) return dbtx;
if(!trans->checkSign())
{
#ifdef DEBUG
std::cerr << "Transaction has bad signature" << std::endl;
#endif
trans->setStatus(INVALID);
return trans;
}
Ledger::TransResult r=theApp->getMasterLedger().getCurrentLedger()->applyTransaction(trans);
if(r==Ledger::TR_ERROR) throw Fault(IO_ERROR);
if((r==Ledger::TR_PREASEQ) || (r==Ledger::TR_BADLSEQ))
{ // transaction should be held
#ifdef DEBUG
std::cerr << "Transaction should be held" << std::endl;
#endif
trans->setStatus(HELD);
theApp->getMasterTransaction().canonicalize(trans, true);
theApp->getMasterLedger().addHeldTransaction(trans);
return trans;
}
if( (r==Ledger::TR_PASTASEQ) || (r==Ledger::TR_ALREADY) )
{ // duplicate or conflict
#ifdef DEBUG
std::cerr << "Transaction is obsolete" << std::endl;
#endif
trans->setStatus(OBSOLETE);
return trans;
}
if(r==Ledger::TR_SUCCESS)
{
#ifdef DEBUG
std::cerr << "Transaction is now included, synching to wallet" << std::endl;
#endif
trans->setStatus(INCLUDED);
theApp->getMasterTransaction().canonicalize(trans, true);
theApp->getWallet().applyTransaction(trans);
newcoin::TMTransaction *tx=new newcoin::TMTransaction();
Serializer::pointer s(trans->getSigned());
tx->set_rawtransaction(&s->getData().front(), s->getLength());
tx->set_status(newcoin::tsCURRENT);
tx->set_receivetimestamp(getNetworkTime());
tx->set_ledgerindexpossible(trans->getLedger());
PackedMessage::pointer packet(new PackedMessage(PackedMessage::MessagePointer(tx), newcoin::mtTRANSACTION));
theApp->getConnectionPool().relayMessage(source, packet);
return trans;
}
#ifdef DEBUG
std::cerr << "Status other than success " << r << std::endl;
#endif
trans->setStatus(INVALID);
return trans;
}
Transaction::pointer NetworkOPs::findTransactionByID(const uint256& transactionID)
{
return Transaction::load(transactionID);
}
int NetworkOPs::findTransactionsBySource(std::list<Transaction::pointer>& txns,
const uint160& sourceAccount, uint32 minSeq, uint32 maxSeq)
{
AccountState::pointer state=getAccountState(sourceAccount);
if(!state) return 0;
if(minSeq>state->getSeq()) return 0;
if(maxSeq>state->getSeq()) maxSeq=state->getSeq();
if(maxSeq>minSeq) return 0;
int count=0;
for(int i=minSeq; i<=maxSeq; i++)
{
Transaction::pointer txn=Transaction::findFrom(sourceAccount, i);
if(txn)
{
txns.push_back(txn);
count++;
}
}
return count;
}
int NetworkOPs::findTransactionsByDestination(std::list<Transaction::pointer>& txns,
const uint160& destinationAccount, uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions)
{
// WRITEME
return 0;
}
AccountState::pointer NetworkOPs::getAccountState(const uint160& accountID)
{
return theApp->getMasterLedger().getCurrentLedger()->getAccountState(accountID);
}

62
src/NetworkOPs.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef __NETWORK_OPS__
#define __NETWORK_OPS__
#include "Transaction.h"
#include "AccountState.h"
// Operations that clients may wish to perform against the network
class Peer;
class NetworkOPs
{
enum Fault
{ // exceptions these functions can throw
IO_ERROR=1,
NO_NETWORK=2,
};
enum OperatingMode
{ // how we process transactions or account balance requests
FAULTED=0, // we are unable to process requests (not ready or no network)
FULL_LOCAL=1, // we are in full local sync
PART_LOCAL=2, // we can validate remote data but have to request it
REMOTE=3 // we have to trust remote nodes
};
public:
// context information
OperatingMode getOperatingMode();
// network information
uint64 getNetworkTime();
uint32 getCurrentLedgerID();
// transaction operations
Transaction::pointer processTransaction(Transaction::pointer transaction, Peer* source=NULL);
Transaction::pointer findTransactionByID(const uint256& transactionID);
int findTransactionsBySource(std::list<Transaction::pointer>&, const uint160& sourceAccount,
uint32 minSeq, uint32 maxSeq);
int findTransactionsByDestination(std::list<Transaction::pointer>&, const uint160& destinationAccount,
uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions);
// account operations
AccountState::pointer getAccountState(const uint160& accountID);
// contact block operations
// 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 synchronzation 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);
};
#endif

27
src/NetworkStatus.h Normal file
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

58
src/NewcoinAddress.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "NewcoinAddress.h"
#include "Config.h"
#include "BitcoinUtil.h"
#include <cassert>
bool NewcoinAddress::SetHash160(const uint160& hash160)
{
SetData(51, &hash160, 20);
return true;
}
bool NewcoinAddress::SetPubKey(const std::vector<unsigned char>& vchPubKey)
{
return SetHash160(Hash160(vchPubKey));
}
bool NewcoinAddress::IsValid()
{
return nVersion == 51 && vchData.size() == 20;
}
NewcoinAddress::NewcoinAddress()
{
}
NewcoinAddress::NewcoinAddress(const uint160& hash160In)
{
SetHash160(hash160In);
}
NewcoinAddress::NewcoinAddress(const std::vector<unsigned char>& vchPubKey)
{
SetPubKey(vchPubKey);
}
NewcoinAddress::NewcoinAddress(const std::string& strAddress)
{
SetString(strAddress);
}
NewcoinAddress::NewcoinAddress(const char* pszAddress)
{
SetString(pszAddress);
}
uint160 NewcoinAddress::GetHash160() const
{
assert(vchData.size() == 20);
uint160 hash160;
memcpy(&hash160, &vchData[0], 20);
return hash160;
}
std::string NewcoinAddress::GetString() const
{
return ToString();
}

27
src/NewcoinAddress.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef __NEWCOIN_ADDRESS__
#define __NEWCOIN_ADDRESS__
#include "base58.h"
// TODO: https://en.bitcoin.it/wiki/Address
// TODO: Why do we need steps 5 and 6? why not just use a checksum function to get the checksum?
class NewcoinAddress : public CBase58Data
{
public:
NewcoinAddress();
NewcoinAddress(const uint160& hash160In);
NewcoinAddress(const std::vector<unsigned char>& vchPubKey);
NewcoinAddress(const std::string& strAddress);
NewcoinAddress(const char* pszAddress);
bool SetHash160(const uint160& hash160);
bool SetPubKey(const std::vector<unsigned char>& vchPubKey);
bool IsValid();
uint160 GetHash160() const;
std::string GetString() const;
};
#endif

60
src/PackedMessage.cpp Normal file
View File

@@ -0,0 +1,60 @@
#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(MessagePointer msg, int type)
: mMsg(msg)
{
unsigned msg_size = mMsg->ByteSize();
assert(msg_size);
mBuffer.resize(HEADER_SIZE + msg_size);
encodeHeader(msg_size,type);
if(msg_size)
{
mMsg->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);
}

77
src/PackedMessage.h Normal file
View File

@@ -0,0 +1,77 @@
//
// 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 "../obj/src/newcoin.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;
MessagePointer mMsg;
PackedMessage(MessagePointer msg, 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 */

685
src/Peer.cpp Normal file
View File

@@ -0,0 +1,685 @@
#include <iostream>
#include <boost/foreach.hpp>
//#include <boost/log/trivial.hpp>
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include "../json/writer.h"
#include "Peer.h"
#include "KnownNodeList.h"
#include "Config.h"
#include "Application.h"
#include "Conversion.h"
Peer::Peer(boost::asio::io_service& io_service)
: mSocket(io_service)
{
}
void Peer::handle_write(const boost::system::error_code& error, size_t bytes_transferred)
{
#ifdef DEBUG
if(error)
std::cout << "Peer::handle_write Error: " << error << " bytes: "<< bytes_transferred << std::endl;
else
std::cout << "Peer::handle_write bytes: "<< bytes_transferred << std::endl;
#endif
mSendingPacket=PackedMessage::pointer();
if(error)
{
detach();
return;
}
if(!mSendQ.empty())
{
PackedMessage::pointer packet=mSendQ.front();
if(packet)
{
sendPacketForce(packet);
mSendQ.pop_front();
}
}
}
void Peer::detach()
{
mSendQ.clear();
mSocket.close();
if(!!mHanko) theApp->getConnectionPool().delFromMap(mHanko);
}
void Peer::connected(const boost::system::error_code& error)
{
if(!error)
{
std::cout << "Connected to Peer." << std::endl; //BOOST_LOG_TRIVIAL(info) << "Connected to Peer.";
sendHello();
start_read_header();
}
else
{
detach();
std::cout << "Peer::connected Error: " << error << std::endl; //else BOOST_LOG_TRIVIAL(info) << "Error: " << error;
}
}
void Peer::sendPacketForce(PackedMessage::pointer packet)
{
mSendingPacket=packet;
boost::asio::async_write(mSocket, boost::asio::buffer(packet->getBuffer()),
boost::bind(&Peer::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void Peer::sendPacket(PackedMessage::pointer packet)
{
if(packet)
{
if(mSendingPacket)
{
mSendQ.push_back(packet);
}
else
{
sendPacketForce(packet);
}
}
}
void Peer::start_read_header()
{
#ifdef DEBUG
std::cerr << "SRH" << std::endl;
#endif
mReadbuf.clear();
mReadbuf.resize(HEADER_SIZE);
boost::asio::async_read(mSocket, boost::asio::buffer(mReadbuf),
boost::bind(&Peer::handle_read_header, shared_from_this(), boost::asio::placeholders::error));
}
void Peer::start_read_body(unsigned msg_len)
{
// m_readbuf already contains the header in its first HEADER_SIZE
// bytes. Expand it to fit in the body as well, and start async
// read into the body.
//
mReadbuf.resize(HEADER_SIZE + msg_len);
boost::asio::async_read(mSocket, boost::asio::buffer(&mReadbuf[HEADER_SIZE], msg_len),
boost::bind(&Peer::handle_read_body, shared_from_this(), boost::asio::placeholders::error));
}
void Peer::handle_read_header(const boost::system::error_code& error)
{
if(!error)
{
unsigned msg_len = PackedMessage::getLength(mReadbuf);
// WRITEME: Compare to maximum message length, abort if too large
if(msg_len>(32*1024*1024))
{
detach();
return;
}
start_read_body(msg_len);
}
else
{
detach();
std::cout << "Peer::connected Error: " << error << std::endl; //else BOOST_LOG_TRIVIAL(info) << "Error: " << error;
}
}
void Peer::handle_read_body(const boost::system::error_code& error)
{
if(!error)
{
processReadBuffer();
start_read_header();
}
else
{
detach();
std::cout << "Peer::connected Error: " << error << std::endl; //else BOOST_LOG_TRIVIAL(info) << "Error: " << error;
}
}
void Peer::processReadBuffer()
{
int type=PackedMessage::getType(mReadbuf);
#ifdef DEBUG
std::cerr << "PRB(" << type << "), len=" << (mReadbuf.size()-HEADER_SIZE) << std::endl;
#endif
switch(type)
{
case newcoin::mtHELLO:
{
newcoin::TMHello msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvHello(msg);
else std::cout << "parse error: " << type << std::endl; //else BOOST_LOG_TRIVIAL(info) << "Error: " << error;
}
break;
case newcoin::mtERROR_MSG:
{
newcoin::TMErrorMsg msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvErrorMessage(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtPING:
{
newcoin::TMPing msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvPing(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtGET_CONTACTS:
{
newcoin::TMGetContacts msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvGetContacts(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtCONTACT:
{
newcoin::TMContact msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvContact(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtSEARCH_TRANSACTION:
{
newcoin::TMSearchTransaction msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvSearchTransaction(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtGET_ACCOUNT:
{
newcoin::TMGetAccount msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvGetAccount(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtACCOUNT:
{
newcoin::TMAccount msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvAccount(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtTRANSACTION:
{
newcoin::TMTransaction msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvTransaction(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtGET_LEDGER:
{
newcoin::TMGetLedger msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvGetLedger(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtLEDGER:
{
newcoin::TMLedgerData msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvLedger(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
#if 0
case newcoin::mtPROPOSE_LEDGER:
{
newcoin::TM msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recv(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtCLOSE_LEDGER:
{
newcoin::TM msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recv(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtGET_VALIDATION:
{
newcoin::TM msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recv(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtVALIDATION:
{
newcoin::TM msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recv(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
#endif
case newcoin::mtGET_OBJECT:
{
newcoin::TMGetObjectByHash msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvGetObjectByHash(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
case newcoin::mtOBJECT:
{
newcoin::TMObjectByHash msg;
if(msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE))
recvObjectByHash(msg);
else std::cout << "pars error: " << type << std::endl;
}
break;
default:
std::cout << "Unknown Msg: " << type << std::endl; //else BOOST_LOG_TRIVIAL(info) << "Error: " << error;
}
}
void Peer::recvHello(newcoin::TMHello& packet)
{
#ifdef DEBUG
std::cerr << "Recv(Hello) v=" << packet.version() << ", index=" << packet.ledgerindex() << std::endl;
#endif
}
void Peer::recvTransaction(newcoin::TMTransaction& packet)
{
#ifdef DEBUG
std::cerr << "Got transaction from peer" << std::endl;
#endif
std::string rawTx=packet.rawtransaction();
std::vector<unsigned char> rTx(rawTx.begin(), rawTx.end());
Transaction::pointer tx=boost::make_shared<Transaction>(rTx, true);
if(tx->getStatus()==INVALID)
{ // transaction fails basic validity tests
#ifdef DEBUG
std::cerr << "Transaction from peer fails validity tests" << std::endl;
Json::StyledStreamWriter w;
w.write(std::cerr, tx->getJson(true));
#endif
return;
}
tx=theApp->getOPs().processTransaction(tx, this);
if(tx->getStatus()!=INCLUDED)
{ // transaction wasn't accepted into ledger
#ifdef DEBUG
std::cerr << "Transaction from peer won't go in ledger" << std::endl;
#endif
}
}
void Peer::recvValidation(newcoin::TMValidation& packet)
{
}
void Peer::recvGetValidation(newcoin::TMGetValidations& packet)
{
}
void Peer::recvContact(newcoin::TMContact& packet)
{
}
void Peer::recvGetContacts(newcoin::TMGetContacts& packet)
{
}
void Peer::recvIndexedObject(newcoin::TMIndexedObject& packet)
{
}
void Peer::recvGetObjectByHash(newcoin::TMGetObjectByHash& packet)
{
}
void Peer::recvObjectByHash(newcoin::TMObjectByHash& packet)
{
}
void Peer::recvPing(newcoin::TMPing& packet)
{
}
void Peer::recvErrorMessage(newcoin::TMErrorMsg& packet)
{
}
void Peer::recvSearchTransaction(newcoin::TMSearchTransaction& packet)
{
}
void Peer::recvGetAccount(newcoin::TMGetAccount& packet)
{
}
void Peer::recvAccount(newcoin::TMAccount& packet)
{
}
void Peer::recvGetLedger(newcoin::TMGetLedger& packet)
{
// Figure out what ledger they want
Ledger::pointer ledger;
if(packet.has_ledgerhash())
{
uint256 ledgerhash;
if(packet.ledgerhash().size()!=32)
{
punishPeer(PP_INVALID_REQUEST);
return;
}
memcpy(&ledgerhash, packet.ledgerhash().data(), 32);
ledger=theApp->getMasterLedger().getLedgerByHash(ledgerhash);
}
else if(packet.has_ledgerseq())
ledger=theApp->getMasterLedger().getLedgerBySeq(packet.ledgerseq());
else if(packet.has_ltype() && (packet.ltype()==newcoin::ltCURRENT) )
ledger=theApp->getMasterLedger().getCurrentLedger();
else if(packet.has_ltype() && (packet.ltype()==newcoin::ltCLOSING) )
{
ledger=theApp->getMasterLedger().getClosingLedger();
}
else if(packet.has_ltype() && (packet.ltype()==newcoin::ltCLOSED) )
{
ledger=theApp->getMasterLedger().getClosingLedger();
if(ledger && !ledger->isClosed())
ledger=theApp->getMasterLedger().getLedgerBySeq(ledger->getLedgerSeq()-1);
}
else
{
punishPeer(PP_INVALID_REQUEST);
return;
}
if( (!ledger) || (packet.has_ledgerseq() && (packet.ledgerseq()!=ledger->getLedgerSeq())) )
{
punishPeer(PP_UNKNOWN_REQUEST);
return;
}
// Figure out what information they want
newcoin::TMLedgerData* data=new newcoin::TMLedgerData;
uint256 lHash=ledger->getHash();
data->set_ledgerhash(lHash.begin(), lHash.size());
data->set_ledgerseq(ledger->getLedgerSeq());
data->set_type(packet.itype());
if(packet.itype()==newcoin::liBASE)
{
Serializer nData(116);
ledger->addRaw(nData);
newcoin::TMLedgerNode* node=data->add_nodes();
node->set_nodedata(nData.getDataPtr(), nData.getLength());
}
else if ( (packet.itype()==newcoin::liTX_NODE) || (packet.itype()==newcoin::liAS_NODE) )
{
SHAMap::pointer map=(packet.itype()==newcoin::liTX_NODE) ? ledger->peekTransactionMap()
: ledger->peekAccountStateMap();
if(!map) return;
if(packet.nodeids_size()==0)
{
punishPeer(PP_INVALID_REQUEST);
return;
}
for(int i=0; i<packet.nodeids().size(); i++)
{
SHAMapNode mn(packet.nodeids(0).data(), packet.nodeids(i).size());
if(!mn.isValid())
{
punishPeer(PP_INVALID_REQUEST);
return;
}
std::vector<SHAMapNode> nodeIDs;
std::list<std::vector<unsigned char> > rawNodes;
if(map->getNodeFat(mn, nodeIDs, rawNodes))
{
std::vector<SHAMapNode>::iterator nodeIDIterator;
std::list<std::vector<unsigned char> >::iterator rawNodeIterator;
for(nodeIDIterator=nodeIDs.begin(), rawNodeIterator=rawNodes.begin();
nodeIDIterator!=nodeIDs.end(); ++nodeIDIterator, ++rawNodeIterator)
{
newcoin::TMLedgerNode* node=data->add_nodes();
Serializer nID(33);
nodeIDIterator->addIDRaw(nID);
node->set_nodeid(nID.getDataPtr(), nID.getLength());
node->set_nodedata(&rawNodeIterator->front(), rawNodeIterator->size());
}
}
}
}
else
{
punishPeer(PP_INVALID_REQUEST);
return;
}
PackedMessage::pointer oPacket=boost::make_shared<PackedMessage>
(PackedMessage::MessagePointer(data), newcoin::mtLEDGER);
sendPacket(oPacket);
}
void Peer::recvLedger(newcoin::TMLedgerData& packet)
{
}
void Peer::sendHello()
{
newcoin::TMHello* h=new newcoin::TMHello();
// set up parameters
h->set_version(theConfig.VERSION);
h->set_ledgerindex(theApp->getOPs().getCurrentLedgerID());
h->set_nettime(theApp->getOPs().getNetworkTime());
h->set_ipv4port(theConfig.PEER_PORT);
Ledger::pointer closingLedger=theApp->getMasterLedger().getClosingLedger();
if(closingLedger->isClosed())
{
Serializer s(128);
closingLedger->addRaw(s);
h->set_closedledger(s.getDataPtr(), s.getLength());
}
PackedMessage::pointer packet=boost::make_shared<PackedMessage>
(PackedMessage::MessagePointer(h), newcoin::mtHELLO);
sendPacket(packet);
}
void Peer::punishPeer(PeerPunish)
{
}
#if 0
/*
PackedMessage::pointer Peer::createFullLedger(Ledger::pointer ledger)
{
if(ledger)
{
// TODO:
newcoin::FullLedger* fullLedger=new newcoin::FullLedger();
ledger->
}
return(PackedMessage::pointer());
}*/
PackedMessage::pointer Peer::createLedgerProposal(Ledger::pointer ledger)
{
uint256& hash=ledger->getHash();
newcoin::ProposeLedger* prop=new newcoin::ProposeLedger();
prop->set_ledgerindex(ledger->getIndex());
prop->set_hash(hash.begin(), hash.GetSerializeSize());
prop->set_numtransactions(ledger->getNumTransactions());
PackedMessage::pointer packet=boost::make_shared<PackedMessage>
(PackedMessage::MessagePointer(prop), newcoin::PROPOSE_LEDGER);
return(packet);
}
PackedMessage::pointer Peer::createValidation(Ledger::pointer ledger)
{
uint256 hash=ledger->getHash();
uint256 sig=ledger->getSignature();
newcoin::Validation* valid=new newcoin::Validation();
valid->set_ledgerindex(ledger->getIndex());
valid->set_hash(hash.begin(), hash.GetSerializeSize());
valid->set_seqnum(ledger->getValidSeqNum());
valid->set_sig(sig.begin(), sig.GetSerializeSize());
valid->set_hanko(theConfig.HANKO);
PackedMessage::pointer packet=boost::make_shared<PackedMessage>
(PackedMessage::MessagePointer(valid), newcoin::VALIDATION);
return(packet);
}
PackedMessage::pointer Peer::createGetFullLedger(uint256& hash)
{
newcoin::GetFullLedger* gfl=new newcoin::GetFullLedger();
gfl->set_hash(hash.begin(), hash.GetSerializeSize());
PackedMessage::pointer packet=boost::make_shared<PackedMessage>
(PackedMessage::MessagePointer(gfl), newcoin::GET_FULL_LEDGER);
return(packet);
}
void Peer::sendLedgerProposal(Ledger::pointer ledger)
{
PackedMessage::pointer packet=Peer::createLedgerProposal(ledger);
sendPacket(packet);
}
void Peer::sendFullLedger(Ledger::pointer ledger)
{
if(ledger)
{
PackedMessage::pointer packet(
new PackedMessage(PackedMessage::MessagePointer(ledger->createFullLedger()), newcoin::FULL_LEDGER));
sendPacket(packet);
}
}
void Peer::sendGetFullLedger(uint256& hash)
{
PackedMessage::pointer packet=createGetFullLedger(hash);
sendPacket(packet);
}
void Peer::receiveHello(newcoin::Hello& packet)
{
// TODO:6 add this guy to your KNL
}
void Peer::receiveGetFullLedger(newcoin::GetFullLedger& gfl)
{
sendFullLedger(theApp->getLedgerMaster().getLedger(protobufTo256(gfl.hash())));
}
void Peer::receiveValidation(newcoin::Validation& validation)
{
theApp->getValidationCollection().addValidation(validation);
}
void Peer::receiveGetValidations(newcoin::GetValidations& request)
{
vector<newcoin::Validation> validations;
theApp->getValidationCollection().getValidations(request.ledgerindex(), validations);
if(validations.size())
{
BOOST_FOREACH(newcoin::Validation& valid, validations)
{
PackedMessage::pointer packet=boost::make_shared<PackedMessage>
(PackedMessage::MessagePointer(new newcoin::Validation(valid)), newcoin::VALIDATION));
sendPacket(packet);
}
}
}
void Peer::receiveTransaction(TransactionPtr trans)
{
// add to the correct transaction bundle and relay if we need to
if(theApp->getLedgerMaster().addTransaction(trans))
{
// broadcast it to other Peers
ConnectionPool& pool=theApp->getConnectionPool();
PackedMessage::pointer packet=boost::make_shread<PackedMessage>
(PackedMessage::MessagePointer(new newcoin::Transaction(*(trans.get()))), newcoin::TRANSACTION);
pool.relayMessage(this, packet);
}
else
{
std::cout << "Invalid transaction: " << trans->from() << std::endl;
}
}
void Peer::receiveProposeLedger(newcoin::ProposeLedger& packet)
{
theApp->getLedgerMaster().checkLedgerProposal(shared_from_this(), packet);
}
void Peer::receiveFullLedger(newcoin::FullLedger& packet)
{
theApp->getLedgerMaster().addFullLedger(packet);
}
void Peer::connectTo(KnownNode& node)
{
tcp::endpoint endpoint( address::from_string(node.mIP), node.mPort);
mSocket.async_connect(endpoint,
boost::bind(&Peer::connected, this, boost::asio::placeholders::error) );
}
#endif

101
src/Peer.h Normal file
View File

@@ -0,0 +1,101 @@
#ifndef __PEER__
#define __PEER__
#include <bitset>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include "../obj/src/newcoin.pb.h"
#include "PackedMessage.h"
#include "Ledger.h"
#include "Transaction.h"
#include "NetworkOPs.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
};
class Peer : public boost::enable_shared_from_this<Peer>
{
public:
static const int psbGotHello=0, psbSentHello=1, psbInMap=2, psbTrusted=3;
static const int psbNoLedgers=4, psbNoTransactions=5, psbDownLevel=6;
protected:
boost::asio::ip::tcp::socket mSocket;
std::vector<uint8_t> mReadbuf;
std::list<PackedMessage::pointer> mSendQ;
PackedMessage::pointer mSendingPacket;
std::bitset<32> mPeerBits;
uint160 mHanko;
Peer(boost::asio::io_service& io_service);
void handle_write(const boost::system::error_code& error, size_t bytes_transferred);
//void handle_read(const boost::system::error_code& error, size_t bytes_transferred);
void handle_read_header(const boost::system::error_code& error);
void handle_read_body(const boost::system::error_code& error);
void processReadBuffer();
void start_read_header();
void start_read_body(unsigned msg_len);
void sendPacketForce(PackedMessage::pointer packet);
void sendHello();
void sendTransaction(newcoin::TMTransaction& packet);
void sendValidation();
void recvHello(newcoin::TMHello& packet);
void recvTransaction(newcoin::TMTransaction& packet);
void recvValidation(newcoin::TMValidation& packet);
void recvGetValidation(newcoin::TMGetValidations& packet);
void recvContact(newcoin::TMContact& packet);
void recvGetContacts(newcoin::TMGetContacts& packet);
void recvIndexedObject(newcoin::TMIndexedObject& packet);
void recvGetObjectByHash(newcoin::TMGetObjectByHash& packet);
void recvObjectByHash(newcoin::TMObjectByHash& packet);
void recvPing(newcoin::TMPing& packet);
void recvErrorMessage(newcoin::TMErrorMsg& packet);
void recvSearchTransaction(newcoin::TMSearchTransaction& packet);
void recvGetAccount(newcoin::TMGetAccount& packet);
void recvAccount(newcoin::TMAccount& packet);
void recvGetLedger(newcoin::TMGetLedger& packet);
void recvLedger(newcoin::TMLedgerData& packet);
public:
typedef boost::shared_ptr<Peer> pointer;
//bool operator == (const Peer& other);
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new Peer(io_service));
}
boost::asio::ip::tcp::socket& getSocket()
{
return mSocket;
}
void connected(const boost::system::error_code& error);
void detach();
bool samePeer(Peer::pointer p) { return samePeer(*p); }
bool samePeer(const Peer& p) { return this == &p; }
void sendPacket(PackedMessage::pointer packet);
void sendLedgerProposal(Ledger::pointer ledger);
void sendFullLedger(Ledger::pointer ledger);
void sendGetFullLedger(uint256& hash);
void punishPeer(PeerPunish pp);
//static PackedMessage::pointer createFullLedger(Ledger::pointer ledger);
static PackedMessage::pointer createLedgerProposal(Ledger::pointer ledger);
static PackedMessage::pointer createValidation(Ledger::pointer ledger);
static PackedMessage::pointer createGetFullLedger(uint256& hash);
};
#endif

36
src/PeerDoor.cpp Normal file
View File

@@ -0,0 +1,36 @@
#include "PeerDoor.h"
#include "Config.h"
#include <boost/bind.hpp>
//#include <boost/log/trivial.hpp>
#include <iostream>
using namespace std;
using namespace boost::asio::ip;
PeerDoor::PeerDoor(boost::asio::io_service& io_service) :
mAcceptor(io_service, tcp::endpoint(tcp::v4(), theConfig.PEER_PORT))
{
cout << "Opening peer door on port: " << theConfig.PEER_PORT << endl;
startListening();
}
void PeerDoor::startListening()
{
Peer::pointer new_connection = Peer::create(mAcceptor.get_io_service());
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; // BOOST_LOG_TRIVIAL(info) << "Error: " << error;
startListening();
}

21
src/PeerDoor.h Normal file
View File

@@ -0,0 +1,21 @@
#include <map>
#include <set>
#include <boost/asio.hpp>
#include "Peer.h"
/*
Handles incoming connections from other Peers
*/
class PeerDoor
{
boost::asio::ip::tcp::acceptor mAcceptor;
void startListening();
void handleConnect(Peer::pointer new_connection,
const boost::system::error_code& error);
public:
PeerDoor(boost::asio::io_service& io_service);
};

74
src/PubKeyCache.cpp Normal file
View File

@@ -0,0 +1,74 @@
#include "PubKeyCache.h"
#include "Application.h"
#include "boost/interprocess/sync/scoped_lock.hpp"
CKey::pointer PubKeyCache::locate(const uint160& id)
{
if(1)
{ // is it in cache
boost::mutex::scoped_lock sl(mLock);
std::map<uint160, 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.GetHex());
sql.append("';'");
std::vector<unsigned char> data;
data.reserve(65); // our public keys are actually 33 bytes
int pkSize;
if(1)
{ // is it in the database
ScopedLock sl(theApp->getTxnDB()->getDBLock());
Database* db=theApp->getTxnDB()->getDB();
if(!db->executeSQL(sql.c_str()) || !db->startIterRows() || !db->getNextRow())
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();
}
if(1)
{ // 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 uint160& id, CKey::pointer key)
{ // stored if needed, returns cached copy (possibly the original)
if(1)
{
boost::mutex::scoped_lock sl(mLock);
std::pair<std::map<uint160,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::string sql="INSERT INTO PubKeys (ID,PubKey) VALUES ('";
sql+=id.GetHex();
sql+="',";
std::vector<unsigned char> pk=key->GetPubKey();
std::string encodedPK;
theApp->getTxnDB()->getDB()->escape(&(pk.front()), pk.size(), encodedPK);
sql+=encodedPK;
sql.append(");");
ScopedLock dbl(theApp->getTxnDB()->getDBLock());
theApp->getTxnDB()->getDB()->executeSQL(sql.c_str(), true);
return key;
}
void PubKeyCache::clear()
{
boost::mutex::scoped_lock sl(mLock);
mCache.empty();
}

25
src/PubKeyCache.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef __PUBKEYCACHE__
#define __PUBKEYCACHE__
#include <map>
#include <boost/thread/mutex.hpp>
#include "uint256.h"
#include "key.h"
class PubKeyCache
{
private:
boost::mutex mLock;
std::map<uint160, CKey::pointer> mCache;
public:
PubKeyCache() { ; }
CKey::pointer locate(const uint160& id);
CKey::pointer store(const uint160& id, CKey::pointer key);
void clear();
};
#endif

39
src/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);

0
src/RPCCommands.cpp Normal file
View File

6
src/RPCCommands.h Normal file
View File

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

49
src/RPCDoor.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "RPCDoor.h"
#include "Config.h"
#include <boost/bind.hpp>
//#include <boost/log/trivial.hpp>
#include <iostream>
using namespace std;
using namespace boost::asio::ip;
RPCDoor::RPCDoor(boost::asio::io_service& io_service) :
mAcceptor(io_service, tcp::endpoint(boost::asio::ip::address_v4::loopback(), theConfig.RPC_PORT))
{
cout << "Opening rpc door on port: " << theConfig.RPC_PORT << endl;
startListening();
}
void RPCDoor::startListening()
{
RPCServer::pointer new_connection = RPCServer::create(mAcceptor.get_io_service());
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(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 cout << "Error: " << error;//BOOST_LOG_TRIVIAL(info) << "Error: " << error;
startListening();
}

18
src/RPCDoor.h Normal file
View File

@@ -0,0 +1,18 @@
#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);
};

537
src/RPCServer.cpp Normal file
View File

@@ -0,0 +1,537 @@
#include <iostream>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include "../json/value.h"
#include "../json/reader.h"
#include "../json/writer.h"
#include "RPCServer.h"
#include "RequestParser.h"
#include "HttpReply.h"
#include "Application.h"
#include "RPC.h"
#include "Wallet.h"
#include "Conversion.h"
#include "LocalTransaction.h"
#include "NewcoinAddress.h"
#include "AccountState.h"
/*
Just read from wire until the entire request is in.
*/
RPCServer::RPCServer(boost::asio::io_service& io_service)
: mSocket(io_service)
{
}
void RPCServer::connected()
{
//BOOST_LOG_TRIVIAL(info) << "RPC request";
std::cout << "RPC request" << std::endl;
mSocket.async_read_some(boost::asio::buffer(mReadBuffer),
boost::bind(&RPCServer::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void RPCServer::handle_read(const boost::system::error_code& e,
std::size_t bytes_transferred)
{
if(!e)
{
boost::tribool result;
result = mRequestParser.parse(
mIncomingRequest, mReadBuffer.data(), mReadBuffer.data() + bytes_transferred);
if(result)
{
mReplyStr=handleRequest(mIncomingRequest.mBody);
sendReply();
}
else if(!result)
{ // bad request
std::cout << "bad request" << std::endl;
}
else
{ // not done keep reading
mSocket.async_read_some(boost::asio::buffer(mReadBuffer),
boost::bind(&RPCServer::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
}
else if(e != boost::asio::error::operation_aborted)
{
}
}
std::string RPCServer::handleRequest(const std::string& requestStr)
{
std::cout << "handleRequest " << requestStr << std::endl;
Json::Value id;
// Parse request
Json::Value valRequest;
Json::Reader reader;
if(!reader.parse(requestStr, valRequest) || valRequest.isNull() || !valRequest.isObject())
return(HTTPReply(400, ""));
// 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, ""));
if (!valMethod.isString())
return(HTTPReply(400, ""));
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, ""));
#ifdef DEBUG
Json::StyledStreamWriter w;
w.write(std::cerr, valParams);
#endif
Json::Value result(doCommand(strMethod, valParams));
#ifdef DEBUG
w.write(std::cerr, result);
#endif
std::string strReply = JSONRPCReply(result, Json::Value(), id);
return( HTTPReply(200, strReply) );
}
int RPCServer::getParamCount(const Json::Value& params)
{ // If non-array, only counts strings
if(params.isNull()) return 0;
if(params.isArray()) return params.size();
if(!params.isConvertibleTo(Json::stringValue))
return 0;
return 1;
}
bool RPCServer::extractString(std::string& param, const Json::Value& params, int index)
{
if(params.isNull()) return false;
if(index!=0)
{
if(!params.isArray() || !params.isValidIndex(index))
return false;
Json::Value p(params.get(index, Json::nullValue));
if(p.isNull() || !p.isConvertibleTo(Json::stringValue))
return false;
param = p.asString();
return true;
}
if(params.isArray())
{
if( (!params.isValidIndex(0)) || (!params[0u].isConvertibleTo(Json::stringValue)) )
return false;
param = params[0u].asString();
return true;
}
if(!params.isConvertibleTo(Json::stringValue))
return false;
param = params.asString();
return true;
}
uint160 RPCServer::parseFamily(const std::string& fParam)
{
uint160 family;
if(Wallet::isHexFamily(fParam))
family.SetHex(fParam);
else if(Wallet::isHexPublicKey(fParam))
family=theApp->getWallet().findFamilyPK(fParam);
else
family=theApp->getWallet().findFamilySN(fParam);
return family;
}
Json::Value RPCServer::doCreateFamily(Json::Value& params)
{
// createfamily <hexPrivateKey>
// createfamily <hexPublicKey>
// createfamily "<pass phrase>"
// createfamily
std::string query;
uint160 family;
uint256 privKey;
if(!extractString(query, params, 0))
family=theApp->getWallet().addRandomFamily(privKey);
else if(Wallet::isHexPrivateKey(query))
family=theApp->getWallet().addFamily(Wallet::textToPrivKey(query), false);
else if(Wallet::isHexPublicKey(query))
family=theApp->getWallet().addFamily(query);
else
family=theApp->getWallet().addFamily(query, false);
if(!family)
return JSONRPCError(500, "Invalid family specifier");
Json::Value ret(theApp->getWallet().getFamilyJson(family));
if(ret.isNull()) return JSONRPCError(500, "Invalid family");
if(!!privKey)
{
ret["PrivateGenerator"]=Wallet::privKeyToText(privKey);
privKey.zero();
}
return ret;
}
Json::Value RPCServer::doAccountInfo(Json::Value &params)
{ // accountinfo <family>:<number>
// accountinfo <account>
std::string acct;
if(!extractString(acct, params, 0))
return JSONRPCError(500, "Invalid account identifier");
LocalAccount::pointer account=theApp->getWallet().parseAccount(acct);
if(account) return account->getJson();
uint160 acctid;
if(AccountState::isHexAccountID(acct)) acctid.SetHex(acct);
else
{
NewcoinAddress ad(acct);
if(ad.IsValid()) acctid=ad.GetHash160();
}
if(!acctid) return JSONRPCError(500, "Unable to parse account");
LocalAccount::pointer lac(theApp->getWallet().getLocalAccount(acctid));
if(!!lac) return lac->getJson();
AccountState::pointer as=theApp->getMasterLedger().getCurrentLedger()->getAccountState(acctid);
Json::Value ret(Json::objectValue);
if(as)
as->addJson(ret);
else
{
NewcoinAddress ad(acctid);
ret[ad.GetString()]="NotFound";
}
return ret;
}
Json::Value RPCServer::doNewAccount(Json::Value &params)
{ // newaccount <family> [<name>]
std::string fParam;
if(!extractString(fParam, params, 0))
return JSONRPCError(500, "Family required");
uint160 family = parseFamily(fParam);
if(!family) return JSONRPCError(500, "No such family");
LocalAccount::pointer account(theApp->getWallet().getNewLocalAccount(family));
if(!account)
return JSONRPCError(500, "Family not found");
return account->getJson();
}
Json::Value RPCServer::doLock(Json::Value &params)
{ // lock <family>
// lock
std::string fParam;
if(extractString(fParam, params, 0))
{ // local <family>
uint160 family = parseFamily(fParam);
if(!family) return JSONRPCError(500, "Family not found");
theApp->getWallet().lock(family);
return "locked";
}
else
{
theApp->getWallet().lock();
return "locked";
}
}
Json::Value RPCServer::doUnlock(Json::Value &params)
{ // unlock <hexPrivateKey>
// unlock "<pass phrase>"
std::string param;
if(!extractString(param, params, 0) || Wallet::isHexPublicKey(param))
return JSONRPCError(500, "Private key required");
uint160 family;
if(Wallet::isHexPrivateKey(param))
family=theApp->getWallet().addFamily(Wallet::textToPrivKey(param), false);
else
family=theApp->getWallet().addFamily(param, false);
if(!family)
return JSONRPCError(500, "Bad family");
Json::Value ret(theApp->getWallet().getFamilyJson(family));
if(ret.isNull()) return JSONRPCError(500, "Invalid family");
return ret;
}
Json::Value RPCServer::doFamilyInfo(Json::Value &params)
{
// familyinfo <family>
// familyinfo <family> <number>
// familyinfo
int paramCount=getParamCount(params);
if(paramCount==0)
{
std::vector<uint160> familyIDs;
theApp->getWallet().getFamilies(familyIDs);
Json::Value ret(Json::arrayValue);
BOOST_FOREACH(const uint160& fid, familyIDs)
{
Json::Value obj(theApp->getWallet().getFamilyJson(fid));
if(!obj.isNull()) ret.append(obj);
}
return ret;
}
if(paramCount>2) return JSONRPCError(500, "Invalid parameters");
std::string fParam;
extractString(fParam, params, 0);
uint160 family=parseFamily(fParam);
if(!family) return JSONRPCError(500, "No such family");
Json::Value obj(theApp->getWallet().getFamilyJson(family));
if(obj.isNull())
return JSONRPCError(500, "Family not found");
if(paramCount==2)
{
std::string keyNum;
extractString(keyNum, params, 1);
int kn=boost::lexical_cast<int>(keyNum);
uint160 k=theApp->getWallet().peekKey(family, kn);
if(!!k)
{
Json::Value key(Json::objectValue);
key["Number"]=kn;
key["Address"]=NewcoinAddress(k).GetString();
obj["Account"]=key;
}
}
return obj;
}
Json::Value RPCServer::doConnect(Json::Value& params)
{
// connect <ip> [port]
std::string host, port;
if(!extractString(host, params, 0))
return JSONRPCError(500, "Host required");
if(!extractString(port, params, 1))
port="6561";
if(!theApp->getConnectionPool().connectTo(host, port))
return JSONRPCError(500, "Unable to connect");
return "connecting";
}
Json::Value RPCServer::doSendTo(Json::Value& params)
{ // Implement simple sending without gathering
// sendto <destination> <amount>
// sendto <destination> <amount> <tag>
if(!params.isArray() || (params.size()<2))
return JSONRPCError(500, "Invalid parameters");
int paramCount=getParamCount(params);
if((paramCount<2)||(paramCount>3))
return JSONRPCError(500, "Invalid parameters");
std::string sDest, sAmount;
if(!extractString(sDest, params, 0) || !extractString(sAmount, params, 1))
return JSONRPCError(500, "Invalid parameters");
uint160 destAccount=parseAccount(sDest);
if(!destAccount)
return JSONRPCError(500, "Unable to parse destination account");
uint64 iAmount;
try
{
iAmount=boost::lexical_cast<uint64>(sAmount);
if(iAmount<=0) return JSONRPCError(500, "Invalid amount");
}
catch (...)
{
return JSONRPCError(500, "Invalid amount");
}
uint32 iTag(0);
try
{
if(paramCount>2)
{
std::string sTag;
extractString(sTag, params, 2);
iTag=boost::lexical_cast<uint32>(sTag);
}
}
catch (...)
{
return JSONRPCError(500, "Invalid tag");
}
#ifdef DEBUG
std::cerr << "SendTo(" << destAccount.GetHex() << ") amount=" << iAmount <<
", tag=" << iTag << std::endl;
#endif
LocalTransaction::pointer lt(new LocalTransaction(destAccount, iAmount, iTag));
if(!lt->makeTransaction())
return JSONRPCError(500, "Insufficient funds in unlocked accounts");
lt->performTransaction();
return lt->getTransaction()->getJson(true);
}
Json::Value RPCServer::doTx(Json::Value& params)
{
// tx
// tx <txID>
// tx <family> <seq>
// tx <account>
std::string param1, param2;
if(!extractString(param1, params, 0))
{ // all local transactions
Json::Value ret(Json::objectValue);
theApp->getWallet().addLocalTransactions(ret);
return ret;
}
if(Transaction::isHexTxID(param1))
{ // transaction by ID
Json::Value ret;
uint256 txid(param1);
if(theApp->getWallet().getTxJson(txid, ret))
return ret;
Transaction::pointer txn=theApp->getMasterTransaction().fetch(txid, true);
if(!txn) return JSONRPCError(500, "Transaction not found");
return txn->getJson(true);
}
if(extractString(param2, params, 1))
{ // family seq
LocalAccount::pointer account=theApp->getWallet().parseAccount(param1+":"+param2);
if(!account)
return JSONRPCError(500, "Account not found");
Json::Value ret;
if(!theApp->getWallet().getTxsJson(account->getAddress(), ret))
return JSONRPCError(500, "Unable to get wallet transactions");
return ret;
}
else
{
// account
}
return "not implemented";
}
Json::Value RPCServer::doLedger(Json::Value& params)
{
// ledger
// ledger <seq>
// ledger <account>
int paramCount=getParamCount(params);
if(paramCount==0);
{
Json::Value ret(Json::objectValue);
theApp->getMasterLedger().getCurrentLedger()->addJson(ret);
theApp->getMasterLedger().getClosingLedger()->addJson(ret);
return ret;
}
return "not implemented";
}
Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params)
{
std::cerr << "RPC:" << command << std::endl;
if(command== "stop")
{
mSocket.get_io_service().stop();
return "newcoin server stopping";
}
if(command== "addUNL")
{
if(params.size()==2)
{
uint160 hanko=humanTo160(params[0u].asString());
std::vector<unsigned char> pubKey;
humanToPK(params[1u].asString(),pubKey);
theApp->getUNL().addNode(hanko,pubKey);
return "adding node";
}
else return "invalid params";
}
if(command=="getUNL")
{
std::string str;
theApp->getUNL().dumpUNL(str);
return(str.c_str());
}
if(command=="createfamily") return doCreateFamily(params);
if(command=="familyinfo") return doFamilyInfo(params);
if(command=="accountinfo") return doAccountInfo(params);
if(command=="newaccount") return doNewAccount(params);
if(command=="lock") return doLock(params);
if(command=="unlock") return doUnlock(params);
if(command=="sendto") return doSendTo(params);
if(command=="connect") return doConnect(params);
if(command=="tx") return doTx(params);
if(command=="ledger") return doLedger(params);
return "unknown command";
}
void RPCServer::sendReply()
{
boost::asio::async_write(mSocket, boost::asio::buffer(mReplyStr),
boost::bind(&RPCServer::handle_write, shared_from_this(),
boost::asio::placeholders::error));
}
void RPCServer::handle_write(const boost::system::error_code& /*error*/)
{
}
uint160 RPCServer::parseAccount(const std::string& account)
{ // FIXME: Support local wallet key names
if(account.find(':')!=std::string::npos)
{ // local account in family:seq form
LocalAccount::pointer lac(theApp->getWallet().parseAccount(account));
if(!lac) return uint160();
return lac->getAddress();
}
NewcoinAddress nac(account);
if(!nac.IsValid()) return uint160();
return nac.GetHash160();
}

66
src/RPCServer.h Normal file
View File

@@ -0,0 +1,66 @@
#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 "RequestParser.h"
#include "uint256.h"
class RPCServer : public boost::enable_shared_from_this<RPCServer>
{
boost::asio::ip::tcp::socket mSocket;
boost::array<char, 8192> mReadBuffer;
std::string mReplyStr;
HttpRequest mIncomingRequest;
HttpRequestParser mRequestParser;
RPCServer(boost::asio::io_service& io_service);
void handle_write(const boost::system::error_code& error);
void handle_read(const boost::system::error_code& e, std::size_t bytes_transferred);
std::string handleRequest(const std::string& requestStr);
void sendReply();
Json::Value doCommand(const std::string& command, Json::Value& params);
int getParamCount(const Json::Value& params);
bool extractString(std::string& param, const Json::Value& params, int index);
uint160 parseFamily(const std::string& family);
Json::Value doCreateFamily(Json::Value& params);
Json::Value doFamilyInfo(Json::Value& params);
Json::Value doAccountInfo(Json::Value& params);
Json::Value doNewAccount(Json::Value& params);
Json::Value doLock(Json::Value& params);
Json::Value doUnlock(Json::Value& params);
Json::Value doSendTo(Json::Value& params);
Json::Value doConnect(Json::Value& params);
Json::Value doTx(Json::Value& params);
Json::Value doLedger(Json::Value& params);
Json::Value doAccount(Json::Value& params);
// parses a string account name into a uint160
// can be local or remote
uint160 parseAccount(const std::string& account);
public:
typedef boost::shared_ptr<RPCServer> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new RPCServer(io_service));
}
boost::asio::ip::tcp::socket& getSocket()
{
return mSocket;
}
void connected();
};

330
src/RequestParser.cpp Normal file
View File

@@ -0,0 +1,330 @@
#include "RequestParser.h"
#include "HttpRequest.h"
HttpRequestParser::HttpRequestParser()
: state_(method_start)
{
}
void HttpRequestParser::reset()
{
state_ = method_start;
}
//template <typename InputIterator>
boost::tribool HttpRequestParser::parse(HttpRequest& req,
char* begin, char* end)
{
while (begin != end)
{
boost::tribool result = consume(req, *begin++);
if (result || !result)
{
std::string temp(begin,end);
req.mBody=temp;
return result;
}
}
boost::tribool result = boost::indeterminate;
return result;
}
boost::tribool HttpRequestParser::consume(HttpRequest& req, char input)
{
switch (state_)
{
case method_start:
if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
state_ = method;
req.method.push_back(input);
return boost::indeterminate;
}
case method:
if (input == ' ')
{
state_ = uri;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
req.method.push_back(input);
return boost::indeterminate;
}
case uri_start:
if (is_ctl(input))
{
return false;
}
else
{
state_ = uri;
req.uri.push_back(input);
return boost::indeterminate;
}
case uri:
if (input == ' ')
{
state_ = http_version_h;
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
req.uri.push_back(input);
return boost::indeterminate;
}
case http_version_h:
if (input == 'H')
{
state_ = http_version_t_1;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_t_1:
if (input == 'T')
{
state_ = http_version_t_2;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_t_2:
if (input == 'T')
{
state_ = http_version_p;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_p:
if (input == 'P')
{
state_ = http_version_slash;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_slash:
if (input == '/')
{
req.http_version_major = 0;
req.http_version_minor = 0;
state_ = http_version_major_start;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_major_start:
if (is_digit(input))
{
req.http_version_major = req.http_version_major * 10 + input - '0';
state_ = http_version_major;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_major:
if (input == '.')
{
state_ = http_version_minor_start;
return boost::indeterminate;
}
else if (is_digit(input))
{
req.http_version_major = req.http_version_major * 10 + input - '0';
return boost::indeterminate;
}
else
{
return false;
}
case http_version_minor_start:
if (is_digit(input))
{
req.http_version_minor = req.http_version_minor * 10 + input - '0';
state_ = http_version_minor;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_minor:
if (input == '\r')
{
state_ = expecting_newline_1;
return boost::indeterminate;
}
else if (is_digit(input))
{
req.http_version_minor = req.http_version_minor * 10 + input - '0';
return boost::indeterminate;
}
else
{
return false;
}
case expecting_newline_1:
if (input == '\n')
{
state_ = header_line_start;
return boost::indeterminate;
}
else
{
return false;
}
case header_line_start:
if (input == '\r')
{
state_ = expecting_newline_3;
return boost::indeterminate;
}
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
{
state_ = header_lws;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
req.headers.push_back(HttpHeader());
req.headers.back().name.push_back(input);
state_ = header_name;
return boost::indeterminate;
}
case header_lws:
if (input == '\r')
{
state_ = expecting_newline_2;
return boost::indeterminate;
}
else if (input == ' ' || input == '\t')
{
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
state_ = header_value;
req.headers.back().value.push_back(input);
return boost::indeterminate;
}
case header_name:
if (input == ':')
{
state_ = space_before_header_value;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
req.headers.back().name.push_back(input);
return boost::indeterminate;
}
case space_before_header_value:
if (input == ' ')
{
state_ = header_value;
return boost::indeterminate;
}
else
{
return false;
}
case header_value:
if (input == '\r')
{
state_ = expecting_newline_2;
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
req.headers.back().value.push_back(input);
return boost::indeterminate;
}
case expecting_newline_2:
if (input == '\n')
{
state_ = header_line_start;
return boost::indeterminate;
}
else
{
return false;
}
case expecting_newline_3:
return (input == '\n');
default:
return false;
}
}
bool HttpRequestParser::is_char(int c)
{
return c >= 0 && c <= 127;
}
bool HttpRequestParser::is_ctl(int c)
{
return (c >= 0 && c <= 31) || (c == 127);
}
bool HttpRequestParser::is_tspecial(int c)
{
switch (c)
{
case '(': case ')': case '<': case '>': case '@':
case ',': case ';': case ':': case '\\': case '"':
case '/': case '[': case ']': case '?': case '=':
case '{': case '}': case ' ': case '\t':
return true;
default:
return false;
}
}
bool HttpRequestParser::is_digit(int c)
{
return c >= '0' && c <= '9';
}

72
src/RequestParser.h Normal file
View File

@@ -0,0 +1,72 @@
#ifndef HTTP_REQUEST_PARSER_HPP
#define HTTP_REQUEST_PARSER_HPP
#include <boost/logic/tribool.hpp>
#include <boost/tuple/tuple.hpp>
struct HttpRequest;
/// Parser for incoming requests.
class HttpRequestParser
{
/// Handle the next character of input.
boost::tribool consume(HttpRequest& req, char input);
/// Check if a byte is an HTTP character.
static bool is_char(int c);
/// Check if a byte is an HTTP control character.
static bool is_ctl(int c);
/// Check if a byte is defined as an HTTP special character.
static bool is_tspecial(int c);
/// Check if a byte is a digit.
static bool is_digit(int c);
/// The current state of the parser.
enum state
{
method_start,
method,
uri_start,
uri,
http_version_h,
http_version_t_1,
http_version_t_2,
http_version_p,
http_version_slash,
http_version_major_start,
http_version_major,
http_version_minor_start,
http_version_minor,
expecting_newline_1,
header_line_start,
header_lws,
header_name,
space_before_header_value,
header_value,
expecting_newline_2,
expecting_newline_3
} state_;
public:
/// Construct ready to parse the request method.
HttpRequestParser();
/// Reset to initial parser state.
void reset();
/// Parse some data. The tribool return value is true when a complete request
/// has been parsed, false if the data is invalid, indeterminate when more
/// data is required. The InputIterator return value indicates how much of the
/// input has been consumed.
//template <typename InputIterator>
boost::tribool parse(HttpRequest& req, char*, char*);
};
#endif // HTTP_REQUEST_PARSER_HPP

679
src/SHAMap.cpp Normal file
View File

@@ -0,0 +1,679 @@
#include <stack>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include "Serializer.h"
#include "BitcoinUtil.h"
#include "SHAMap.h"
SHAMap::SHAMap(uint32 seq) : mSeq(seq), mState(Modifying)
{
root=boost::make_shared<SHAMapTreeNode>(SHAMapNode(0, uint256()), mSeq);
root->makeInner();
mTNByID[*root]=root;
}
std::stack<SHAMapTreeNode::pointer> SHAMap::getStack(const uint256& id, bool include_nonmatching_leaf)
{
// 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;
node=getNode(node->getChildNodeID(branch), hash, false);
if(!node)
{
if(isSynching()) return stack;
throw SHAMapException(MissingNode);
}
}
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!=Synching && mState!=Immutable);
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))
{
std::cerr << "dirtyUp terminates early" << std::endl;
assert(false);
return;
}
#ifdef ST_DEBUG
std::cerr << "dirtyUp sets branch " << branch << " to " << prevHash.GetHex() << std::endl;
#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);
if(childHash.isZero()) return inNode;
SHAMapTreeNode::pointer nextNode=getNode(inNode->getChildNodeID(branch), childHash, false);
if(!nextNode) throw SHAMapException(MissingNode);
inNode=nextNode;
}
if(modify) returnNode(inNode, true);
return inNode;
}
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)
{
if(node->getNodeHash()!=hash)
{
#ifdef DEBUG
std::cerr << "Attempt to get node, hash not in tree" << std::endl;
std::cerr << "ID: " << id.getString() << std::endl;
std::cerr << "TgtHash " << hash.GetHex() << std::endl;
std::cerr << "NodHash " << node->getNodeHash().GetHex() << std::endl;
dump();
#endif
throw SHAMapException(InvalidNode);
}
returnNode(node, modify);
return node;
}
std::vector<unsigned char> nodeData;
if(!fetchNode(hash, nodeData)) return SHAMapTreeNode::pointer();
node=boost::make_shared<SHAMapTreeNode>(id, nodeData, mSeq);
if(node->getNodeHash()!=hash) throw SHAMapException(InvalidNode);
if(!mTNByID.insert(std::make_pair(id, node)).second)
assert(false);
return node;
}
void SHAMap::returnNode(SHAMapTreeNode::pointer& node, bool modify)
{ // make sure the node is suitable for the intended operation (copy on write)
assert(node->isValid());
if(node && modify && (node->getSeq()!=mSeq))
{
#ifdef DEBUG
std::cerr << "returnNode COW" << std::endl;
#endif
if(mDirtyNodes) (*mDirtyNodes)[*node]=node;
node=boost::make_shared<SHAMapTreeNode>(*node, mSeq);
assert(node->isValid());
mTNByID[*node]=node;
}
}
SHAMapItem::SHAMapItem(const uint256& tag, const std::vector<unsigned char>& data)
: mTag(tag), mData(data)
{ ; }
SHAMapItem::SHAMapItem(const uint160& tag, const std::vector<unsigned char>& data)
: mTag(tag.to256()), mData(data)
{ ; }
SHAMapItem::pointer SHAMap::firstBelow(SHAMapTreeNode::pointer node)
{
// Return the first item below this node
#ifdef ST_DEBUG
std::cerr << "firstBelow(" << node->getString() << ")" << std::endl;
#endif
do
{ // Walk down the tree
if(node->hasItem()) return node->peekItem();
bool foundNode=false;
for(int i=0; i<16; i++)
if(!node->isEmptyBranch(i))
{
#ifdef ST_DEBUG
std::cerr << " FB: node " << node->getString() << std::endl;
std::cerr << " has non-empty branch " << i << " : " <<
node->getChildNodeID(i).getString() << ", " << node->getChildHash(i).GetHex() << std::endl;
#endif
node=getNode(node->getChildNodeID(i), node->getChildHash(i), false);
if(!node) throw SHAMapException(MissingNode);
foundNode=true;
break;
}
if(!foundNode) return SHAMapItem::pointer();
} while(1);
}
SHAMapItem::pointer SHAMap::lastBelow(SHAMapTreeNode::pointer node)
{
#ifdef DEBUG
std::cerr << "lastBelow(" << node->getString() << ")" << std::endl;
#endif
do
{ // Walk down the tree
if(node->hasItem()) return node->peekItem();
bool foundNode=false;
for(int i=15; i>=0; i++)
if(!node->isEmptyBranch(i))
{
node=getNode(node->getChildNodeID(i), node->getChildHash(i), false);
if(!node) throw SHAMapException(MissingNode);
foundNode=true;
break;
}
if(!foundNode) return SHAMapItem::pointer();
} while(1);
}
SHAMapItem::pointer SHAMap::onlyBelow(SHAMapTreeNode::pointer node)
{
// If there is only one item below this node, return it
bool found;
while(!node->isLeaf())
{
found=false;
SHAMapTreeNode::pointer nextNode;
for(int i=0; i<16; i++)
if(!node->isEmptyBranch(i))
{
if(found) return SHAMapItem::pointer(); // two leaves below
nextNode=getNode(node->getChildNodeID(i), node->getChildHash(i), false);
if(!nextNode) throw SHAMapException(MissingNode);
found=true;
}
if(!found)
{
std::cerr << node->getString() << 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);
return firstBelow(root);
}
SHAMapItem::pointer SHAMap::peekLastItem()
{
boost::recursive_mutex::scoped_lock sl(mLock);
return lastBelow(root);
}
SHAMapItem::pointer SHAMap::peekNextItem(const uint256& id)
{ // 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);
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<16; i++)
if(!node->isEmptyBranch(i))
{
node=getNode(node->getChildNodeID(i), node->getChildHash(i), false);
if(!node) throw SHAMapException(MissingNode);
SHAMapItem::pointer item=firstBelow(node);
if(!item) throw SHAMapException(MissingNode);
return item;
}
}
// 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);
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);
if(!node) throw SHAMapException(MissingNode);
SHAMapItem::pointer item=firstBelow(node);
if(!item) throw SHAMapException(MissingNode);
return item;
}
}
// must be last item
return SHAMapItem::pointer();
}
SHAMapItem::pointer SHAMap::peekItem(const uint256& id)
{
boost::recursive_mutex::scoped_lock sl(mLock);
SHAMapTreeNode::pointer leaf=walkTo(id, false);
if(!leaf) return SHAMapItem::pointer();
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::pointer leaf=walkTo(id, false);
if(!leaf) return false;
SHAMapItem::pointer item=leaf->peekItem();
if(!item || item->getTag()!=id) return false;
return true;
}
bool SHAMap::delItem(const uint256& id)
{ // delete the item with this ID
boost::recursive_mutex::scoped_lock sl(mLock);
std::stack<SHAMapTreeNode::pointer> stack=getStack(id, true);
if(stack.empty()) throw SHAMapException(MissingNode);
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);
if(item)
{
eraseChildren(node);
#ifdef ST_DEBUG
std::cerr << "Making item node " << node->getString() << 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::pointer item, bool isTransaction)
{ // add the specified item, does not update
#ifdef ST_DEBUG
std::cerr << "aGI " << item->getTag().GetHex() << std::endl;
#endif
uint256 tag=item->getTag();
SHAMapTreeNode::TNType type=isTransaction ? SHAMapTreeNode::tnTRANSACTION : SHAMapTreeNode::tnACCOUNT_STATE;
boost::recursive_mutex::scoped_lock sl(mLock);
std::stack<SHAMapTreeNode::pointer> stack=getStack(tag, true);
if(stack.empty()) throw SHAMapException(MissingNode);
SHAMapTreeNode::pointer node=stack.top();
stack.pop();
if( node->isLeaf() && (node->peekItem()->getTag()==tag) )
{
std::cerr << "addGiveItem ends on leaf with same tag" << std::endl;
return false;
}
uint256 prevHash;
returnNode(node, true);
if(node->isInner())
{ // easy case, we end on an inner node
#ifdef ST_DEBUG
std::cerr << "aGI inner " << node->getString() << 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->getString() << std::endl;
std::cerr << "NewNode: " << newNode->getString() << std::endl;
assert(false);
throw SHAMapException(InvalidNode);
}
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->getString() << 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() << std::endl;
#endif
SHAMapTreeNode::pointer newNode=boost::make_shared<SHAMapTreeNode>(node->getChildNodeID(b1), mSeq);
newNode->makeInner();
if(!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second)
assert(false);
stack.push(node);
node=newNode;
}
// 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
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());
}
prevHash=node->getNodeHash();
assert(prevHash.isNonZero());
dirtyUp(stack, tag, prevHash);
return true;
}
bool SHAMap::addItem(const SHAMapItem& i, bool isTransaction)
{
return addGiveItem(boost::make_shared<SHAMapItem>(i), isTransaction);
}
bool SHAMap::updateGiveItem(SHAMapItem::pointer item, bool isTransaction)
{ // can't change the tag but can change the hash
uint256 tag=item->getTag();
boost::recursive_mutex::scoped_lock sl(mLock);
std::stack<SHAMapTreeNode::pointer> stack=getStack(tag, true);
if(stack.empty()) throw SHAMapException(MissingNode);
SHAMapTreeNode::pointer node=stack.top();
stack.pop();
if(!node->isLeaf() || (node->peekItem()->getTag()==tag) )
return false;
returnNode(node, true);
if(!node->setItem(item, isTransaction ? SHAMapTreeNode::tnTRANSACTION : SHAMapTreeNode::tnACCOUNT_STATE))
return true;
dirtyUp(stack, tag, node->getNodeHash());
return true;
}
void SHAMapItem::dump()
{
std::cerr << "SHAMapItem(" << mTag.GetHex() << ") " << mData.size() << "bytes" << std::endl;
}
bool SHAMap::fetchNode(const uint256& hash, std::vector<unsigned char>& data)
{
HashedObject::pointer obj(HashedObject::retrieve(hash));
if(!obj) return false;
data=obj->getData();
return true;
}
int SHAMap::flushDirty(int maxNodes, HashedObjectType t, uint32 seq)
{
int flushed=0;
Serializer s;
if(mDirtyNodes)
{
while(!mDirtyNodes->empty())
{
SHAMapTreeNode::pointer& din=mDirtyNodes->begin()->second;
s.erase();
din->addRaw(s);
HashedObject::store(t, seq, s.peekData(), s.getSHA512Half());
mDirtyNodes->erase(mDirtyNodes->begin());
if(flushed++>=maxNodes) return flushed;
}
}
return flushed;
}
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 SHAMapException(MissingNode);
}
return node;
}
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().GetHex() << 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, hash_SMN>::iterator it=mTNByID.begin();
it!=mTNByID.end(); ++it)
{
std::cerr << it->second->getString() << std::endl;
if(hash) std::cerr << " " << it->second->getNodeHash().GetHex() << std::endl;
}
}
static std::vector<unsigned char>IntToVUC(int i)
{
std::vector<unsigned char> vuc;
for(int i=0; i<32; i++)
vuc.push_back((unsigned char) i);
return vuc;
}
bool SHAMap::TestSHAMap()
{ // h3 and h4 differ only in the leaf, same terminal node (level 19)
uint256 h1, h2, h3, h4, h5;
h1.SetHex("092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
h2.SetHex("436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
h3.SetHex("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
h4.SetHex("b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
h5.SetHex("a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
SHAMap sMap;
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))
{
assert(false);
return false;
}
if(!sMap.addItem(i1, true))
{
assert(false);
return false;
}
SHAMapItem::pointer i;
i=sMap.peekFirstItem();
assert(!!i && (*i==i1));
i=sMap.peekNextItem(i->getTag());
assert(!!i && (*i==i2));
i=sMap.peekNextItem(i->getTag());
assert(!i);
sMap.addItem(i4, true);
sMap.delItem(i2.getTag());
sMap.addItem(i3, true);
i=sMap.peekFirstItem();
assert(!!i && (*i==i1));
i=sMap.peekNextItem(i->getTag());
assert(!!i && (*i==i3));
i=sMap.peekNextItem(i->getTag());
assert(!!i && (*i==i4));
i=sMap.peekNextItem(i->getTag());
assert(!i);
if(!syncTest())
return false;
return true;
}

329
src/SHAMap.h Normal file
View File

@@ -0,0 +1,329 @@
#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"
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
public:
typedef boost::shared_ptr<SHAMapNode> pointer;
private:
static uint256 smMasks[64]; // 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);
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 void 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);
};
class hash_SMN
{ // These must be randomized for release
public:
std::size_t operator() (const SHAMapNode& mn) const
{ return mn.getDepth() ^ static_cast<std::size_t>(mn.getNodeID().GetAt(0)); }
std::size_t operator() (const uint256& u) const
{ return static_cast<std::size_t>(u.GetAt(0)); }
};
class SHAMapItem
{ // an item stored in a SHAMap
public:
typedef boost::shared_ptr<SHAMapItem> pointer;
private:
uint256 mTag;
std::vector<unsigned char> mData;
public:
// for transactions
SHAMapItem(const uint256& tag, const std::vector<unsigned char>& data);
SHAMapItem(const std::vector<unsigned char>& data); // tag by hash
// for account balances
SHAMapItem(const uint160& tag, const std::vector<unsigned char>& data);
const uint256& getTag() const { return mTag; }
std::vector<unsigned char> getData() const { return mData; }
const std::vector<unsigned char>& peekData() const { return mData; }
void addRaw(Serializer &s) { s.addRaw(mData); }
void addRaw(std::vector<unsigned char>& s) { 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 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; }
bool operator<=(const uint256& i) const { return mTag<=i; }
bool operator>=(const uint256& i) const { return mTag>=i; }
virtual void dump();
};
class SHAMapTreeNode : public SHAMapNode
{
friend class SHAMap;
public:
typedef boost::shared_ptr<SHAMapTreeNode> pointer;
enum TNType
{
tnERROR =0,
tnINNER =1,
tnTRANSACTION =2,
tnACCOUNT_STATE =3
};
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(const SHAMapNode& nodeID, uint32 seq); // empty node
SHAMapTreeNode(const SHAMapTreeNode& node, uint32 seq); // copy node from older tree
SHAMapTreeNode(const SHAMapNode& nodeID, SHAMapItem::pointer item, TNType type, uint32 seq);
// raw node functions
SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned char>& contents, uint32 seq); // raw node
void addRaw(Serializer &);
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) || (mType==tnACCOUNT_STATE); }
bool isInner() const { return mType==tnINNER; }
bool isValid() const { return mType!=tnERROR; }
bool isTransaction() const { return mType!=tnTRANSACTION; }
bool isAccountState() const { return mType!=tnACCOUNT_STATE; }
// inner node functions
bool isInnerNode() const { return !mItem; }
bool setChildHash(int m, const uint256& hash);
const uint256& getChildHash(int m) const;
bool isEmptyBranch(int m) const { return !mHashes[m]; }
int getBranchCount() const;
void makeInner();
// item node function
bool hasItem() const { return !!mItem; }
SHAMapItem::pointer peekItem() { return mItem; }
SHAMapItem::pointer getItem() const;
bool setItem(SHAMapItem::pointer& 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 SHAMapException
{
MissingNode=1,
InvalidNode=2,
InvalidMap=3,
};
enum SHAMapState
{
Modifying=0, // Objects can be added and removed (like an open ledger)
Immutable=1, // Map cannot be changed (like a closed ledger)
Synching=2, // Map's hash is locked in, valid nodes can be added (like a peer's closing ledger)
Floating=3, // Map is free to change hash (like a synching open ledger)
Invalid=4, // Map is known not to be valid (usually synching a corrupt ledger)
};
class SHAMap
{
public:
typedef boost::shared_ptr<SHAMap> pointer;
typedef std::map<uint256, std::pair<SHAMapItem::pointer, SHAMapItem::pointer> > SHAMapDiff;
private:
uint32 mSeq;
mutable boost::recursive_mutex mLock;
boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer, hash_SMN> mTNByID;
boost::shared_ptr<std::map<SHAMapNode, SHAMapTreeNode::pointer> > mDirtyNodes;
SHAMapTreeNode::pointer root;
SHAMapState mState;
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);
SHAMapTreeNode::pointer walkTo(const uint256& id, bool modify);
SHAMapTreeNode::pointer checkCacheNode(const SHAMapNode&);
void returnNode(SHAMapTreeNode::pointer&, bool modify);
SHAMapTreeNode::pointer getNode(const SHAMapNode& id);
SHAMapTreeNode::pointer getNode(const SHAMapNode& id, const uint256& hash, bool modify);
SHAMapItem::pointer firstBelow(SHAMapTreeNode::pointer);
SHAMapItem::pointer lastBelow(SHAMapTreeNode::pointer);
SHAMapItem::pointer onlyBelow(SHAMapTreeNode::pointer);
void eraseChildren(SHAMapTreeNode::pointer);
bool walkBranch(SHAMapTreeNode::pointer node, SHAMapItem::pointer otherMapItem, bool isFirstMap,
SHAMapDiff& differences, int& maxCount);
public:
// build new map
SHAMap(uint32 seq=0);
// hold the map stable across operations
ScopedLock Lock() const { return ScopedLock(mLock); }
bool hasNode(const SHAMapNode& id);
// normal hash access functions
bool hasItem(const uint256& id);
bool delItem(const uint256& id);
bool addItem(const SHAMapItem& i, bool isTransaction);
bool updateItem(const SHAMapItem& i, bool isTransaction);
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::pointer, bool isTransaction);
bool addGiveItem(SHAMapItem::pointer, bool isTransaction);
// save a copy if you only need a temporary
SHAMapItem::pointer peekItem(const uint256& id);
// traverse functions
SHAMapItem::pointer peekFirstItem();
SHAMapItem::pointer peekLastItem();
SHAMapItem::pointer peekNextItem(const uint256&);
SHAMapItem::pointer peekPrevItem(const uint256&);
SHAMapItem::pointer peekPrevItem(const uint160& u) { return peekPrevItem(u.to256()); }
SHAMapItem::pointer peekNextItem(const uint160& u) { return peekNextItem(u.to256()); }
// comparison/sync functions
void getMissingNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max);
bool getNodeFat(const SHAMapNode& node, std::vector<SHAMapNode>& nodeIDs,
std::list<std::vector<unsigned char> >& rawNode);
bool addRootNode(const uint256& hash, const std::vector<unsigned char>& rootNode);
bool addRootNode(const std::vector<unsigned char>& rootNode);
bool addKnownNode(const SHAMapNode& nodeID, const std::vector<unsigned char>& rawNode);
// status functions
void setImmutable(void) { assert(mState!=Invalid); mState=Immutable; }
void clearImmutable(void) { mState=Modifying; }
bool isSynching(void) const { return mState==Floating || mState==Synching; }
void setSynching(void) { mState=Synching; }
void setFloating(void) { mState=Floating; }
void clearSynching(void) { mState=Modifying; }
bool isValid(void) { return mState!=Invalid; }
// caution: otherMap must be accessed only by this function
// return value: true=successfully completed, false=too different
bool compare(SHAMap::pointer otherMap, SHAMapDiff& differences, int maxCount);
int flushDirty(int maxNodes, HashedObjectType t, uint32 seq);
void setSeq(uint32 seq) { mSeq=seq; }
uint32 getSeq() { return mSeq; }
// overloads for backed maps
bool fetchNode(const uint256& hash, std::vector<unsigned char>& rawNode);
bool operator==(const SHAMap& s) { return getHash()==s.getHash(); }
static bool TestSHAMap();
static bool syncTest();
bool deepCompare(SHAMap& other);
virtual void dump(bool withHashes=false);
};
#endif

181
src/SHAMapDiff.cpp Normal file
View File

@@ -0,0 +1,181 @@
#include "SHAMap.h"
#include <stack>
// This code is used to compare another node's transaction tree
// to our own. It returns a map containing all items that are different
// between two SHA maps. It is optimized not to descend down tree
// branches with the same branch hash. A limit can be passed so
// that we will abort early if a node sends a map to us that
// makes no sense at all. (And our sync algorithm will avoid
// synchronizing matching brances too.)
class SHAMapDiffNode
{
public:
SHAMapNode mNodeID;
uint256 mOurHash, mOtherHash;
SHAMapDiffNode(SHAMapNode id, const uint256& ourHash, const uint256& otherHash) :
mNodeID(id), mOurHash(ourHash), mOtherHash(otherHash) { ; }
};
bool SHAMap::walkBranch(SHAMapTreeNode::pointer node, SHAMapItem::pointer otherMapItem, bool isFirstMap,
SHAMapDiff& differences, int& maxCount)
{
// Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map
std::stack<SHAMapTreeNode::pointer> nodeStack;
nodeStack.push(node);
while(!nodeStack.empty())
{
SHAMapTreeNode::pointer node=nodeStack.top();
nodeStack.pop();
if(node->isInner())
{ // This is an inner node, add all non-empty branches
for(int i=0; i<16; i++)
if(!node->isEmptyBranch(i))
{
SHAMapTreeNode::pointer newNode=getNode(node->getChildNodeID(i), node->getChildHash(i), false);
if(!newNode) throw SHAMapException(MissingNode);
nodeStack.push(newNode);
}
}
else
{ // This is a leaf node, process its item
SHAMapItem::pointer item=node->getItem();
if(otherMapItem && otherMapItem->getTag()<item->getTag())
{ // this item comes after the item from the other map, so add the other item
if(isFirstMap) // this is first map, so other item is from second
differences.insert(std::make_pair(otherMapItem->getTag(),
std::make_pair(SHAMapItem::pointer(), otherMapItem)));
else
differences.insert(std::make_pair(otherMapItem->getTag(),
std::make_pair(otherMapItem, SHAMapItem::pointer())));
if((--maxCount)<=0) return false;
otherMapItem=SHAMapItem::pointer();
}
if( (!otherMapItem) || (item->getTag()<otherMapItem->getTag()) )
{ // unmatched
if(isFirstMap)
differences.insert(std::make_pair(item->getTag(), std::make_pair(item, SHAMapItem::pointer())));
else
differences.insert(std::make_pair(item->getTag(), std::make_pair(SHAMapItem::pointer(), item)));
if((--maxCount)<=0) return false;
}
else if(item->getTag()==otherMapItem->getTag())
{
if(item->peekData()!=otherMapItem->peekData())
{ // non-matching items
if(isFirstMap)
differences.insert(std::make_pair(otherMapItem->getTag(),
std::make_pair(item, otherMapItem)));
else
differences.insert(std::make_pair(otherMapItem->getTag(),
std::make_pair(otherMapItem, item)));
if((--maxCount)<=0) return false;
item=SHAMapItem::pointer();
}
}
else assert(false);
}
}
if(otherMapItem)
{ // otherMapItem was unmatched, must add
if(isFirstMap) // this is first map, so other item is from second
differences.insert(std::make_pair(otherMapItem->getTag(),
std::make_pair(SHAMapItem::pointer(), otherMapItem)));
else
differences.insert(std::make_pair(otherMapItem->getTag(),
std::make_pair(otherMapItem, SHAMapItem::pointer())));
if((--maxCount)<=0) return false;
}
return true;
}
bool SHAMap::compare(SHAMap::pointer otherMap, SHAMapDiff& differences, int maxCount)
{ // compare two hash trees, add up to maxCount differences to the difference table
// return value: true=complete table of differences given, false=too many differences
// throws on corrupt tables or missing nodes
std::stack<SHAMapDiffNode> nodeStack; // track nodes we've pushed
ScopedLock sl(Lock());
if(getHash() == otherMap->getHash()) return true;
nodeStack.push(SHAMapDiffNode(SHAMapNode(), getHash(), otherMap->getHash()));
while(!nodeStack.empty())
{
SHAMapDiffNode dNode(nodeStack.top());
nodeStack.pop();
SHAMapTreeNode::pointer ourNode=getNode(dNode.mNodeID, dNode.mOurHash, false);
SHAMapTreeNode::pointer otherNode=otherMap->getNode(dNode.mNodeID, dNode.mOtherHash, false);
if(!ourNode || !otherNode) throw SHAMapException(MissingNode);
if(ourNode->isLeaf() && otherNode->isLeaf())
{ // two leaves
if(ourNode->getTag() == otherNode->getTag())
{
if(ourNode->peekData()!=otherNode->peekData())
{
differences.insert(std::make_pair(ourNode->getTag(),
std::make_pair(ourNode->getItem(), otherNode->getItem())));
if((--maxCount)<=0) return false;
}
}
else
{
differences.insert(std::make_pair(ourNode->getTag(),
std::make_pair(ourNode->getItem(), SHAMapItem::pointer())));
if((--maxCount)<=0) return false;
differences.insert(std::make_pair(otherNode->getTag(),
std::make_pair(SHAMapItem::pointer(), otherNode->getItem())));
if((--maxCount)<=0) return false;
}
}
else if(ourNode->isInner() && otherNode->isLeaf())
{
if(!walkBranch(ourNode, otherNode->getItem(), true, differences, maxCount))
return false;
}
else if(ourNode->isLeaf() && otherNode->isInner())
{
if(!otherMap->walkBranch(otherNode, ourNode->getItem(), false, differences, maxCount))
return false;
}
else if(ourNode->isInner() && otherNode->isInner())
{
for(int i=0; i<16; i++)
if(ourNode->getChildHash(i) != otherNode->getChildHash(i) )
{
if(!otherNode->getChildHash(i))
{ // We have a branch, the other tree does not
SHAMapTreeNode::pointer iNode=getNode(ourNode->getChildNodeID(i),
ourNode->getChildHash(i), false);
if(!iNode) throw SHAMapException(MissingNode);
if(!walkBranch(iNode, SHAMapItem::pointer(), true, differences, maxCount))
return false;
}
else if(!ourNode->getChildHash(i))
{ // The other tree has a branch, we do not
SHAMapTreeNode::pointer iNode=otherMap->getNode(otherNode->getChildNodeID(i),
otherNode->getChildHash(i), false);
if(!iNode) throw SHAMapException(MissingNode);
if(!otherMap->walkBranch(iNode, SHAMapItem::pointer(), false, differences, maxCount))
return false;
}
else // The two trees have different non-empty branches
nodeStack.push(SHAMapDiffNode(ourNode->getChildNodeID(i),
ourNode->getChildHash(i), otherNode->getChildHash(i)));
}
}
else assert(false);
}
return true;
}

370
src/SHAMapNodes.cpp Normal file
View File

@@ -0,0 +1,370 @@
#include <cstring>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <openssl/sha.h>
#include "Serializer.h"
#include "BitcoinUtil.h"
#include "SHAMap.h"
std::string SHAMapNode::getString() const
{
std::string ret="NodeID(";
ret+=boost::lexical_cast<std::string>(mDepth);
ret+=",";
ret+=mNodeID.GetHex();
ret+=")";
return ret;
}
uint256 SHAMapNode::smMasks[64];
bool SHAMapNode::operator<(const SHAMapNode &s) const
{
if(s.mDepth<mDepth) return true;
if(s.mDepth>mDepth) return false;
return mNodeID<s.mNodeID;
}
bool SHAMapNode::operator>(const SHAMapNode &s) const
{
if(s.mDepth<mDepth) return false;
if(s.mDepth>mDepth) return true;
return mNodeID>s.mNodeID;
}
bool SHAMapNode::operator<=(const SHAMapNode &s) const
{
if(s.mDepth<mDepth) return true;
if(s.mDepth>mDepth) return false;
return mNodeID<=s.mNodeID;
}
bool SHAMapNode::operator>=(const SHAMapNode &s) const
{
if(s.mDepth<mDepth) return false;
if(s.mDepth>mDepth) return true;
return mNodeID>=s.mNodeID;
}
bool SHAMapNode::operator==(const SHAMapNode &s) const
{
return (s.mDepth==mDepth) && (s.mNodeID==mNodeID);
}
bool SHAMapNode::operator!=(const SHAMapNode &s) const
{
return (s.mDepth!=mDepth) || (s.mNodeID!=mNodeID);
}
bool SHAMapNode::operator==(const uint256 &s) const
{
return s==mNodeID;
}
bool SHAMapNode::operator!=(const uint256 &s) const
{
return s!=mNodeID;
}
void SHAMapNode::ClassInit()
{ // set up the depth masks
uint256 selector;
for(int i=0; i<64; i+=2)
{
smMasks[i]=selector;
*(selector.begin()+(i/2))=0x0F;
smMasks[i+1]=selector;
*(selector.begin()+(i/2))=0xFF;
}
}
uint256 SHAMapNode::getNodeID(int depth, const uint256& hash)
{
assert(depth>=0 && depth<64);
return hash & smMasks[depth];
}
SHAMapNode::SHAMapNode(int depth, const uint256 &hash) : mDepth(depth)
{ // canonicalize the hash to a node ID for this depth
assert(depth>=0 && depth<64);
mNodeID = getNodeID(depth, hash);
}
SHAMapNode::SHAMapNode(const void *ptr, int len)
{
if(len<33) mDepth=-1;
else
{
memcpy(&mNodeID, ptr, 32);
mDepth=*(static_cast<const unsigned char *>(ptr) + 32);
}
}
void SHAMapNode::addIDRaw(Serializer &s) const
{
s.add256(mNodeID);
s.add8(mDepth);
}
std::string SHAMapNode::getRawString() const
{
Serializer s(33);
addIDRaw(s);
return s.getString();
}
SHAMapNode SHAMapNode::getChildNodeID(int m) const
{ // This can be optimized to avoid the << if needed
assert((m>=0) && (m<16));
uint256 child(mNodeID);
child.PeekAt(mDepth/8) |= m << (4*(mDepth%8));
return SHAMapNode(mDepth+1, child);
}
int SHAMapNode::selectBranch(const uint256& hash) const
{ // Which branch would contain the specified hash
if(mDepth==63)
{
assert(false);
return -1;
}
if((hash&smMasks[mDepth])!=mNodeID)
{
std::cerr << "selectBranch(" << getString() << std::endl;
std::cerr << " " << hash.GetHex() << " off branch" << std::endl;
assert(false);
return -1; // does not go under this node
}
int branch=*(hash.begin()+(mDepth/2));
if(mDepth%2) branch>>=4;
else branch&=0xf;
assert(branch>=0 && branch<16);
return branch;
}
void SHAMapNode::dump() const
{
std::cerr << getString() << std::endl;
}
SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& nodeID, uint32 seq) : SHAMapNode(nodeID), mHash(0), mSeq(seq),
mType(tnERROR), mFullBelow(false)
{
}
SHAMapTreeNode::SHAMapTreeNode(const SHAMapTreeNode& node, uint32 seq) : SHAMapNode(node),
mHash(node.mHash), mItem(node.mItem), mSeq(seq), mType(node.mType), mFullBelow(false)
{
if(node.mItem)
mItem=boost::make_shared<SHAMapItem>(*node.mItem);
else
memcpy(mHashes, node.mHashes, sizeof(mHashes));
}
SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& node, SHAMapItem::pointer item, TNType type, uint32 seq) :
SHAMapNode(node), mItem(item), mSeq(seq), mType(type), mFullBelow(true)
{
assert(item->peekData().size()>=12);
updateHash();
}
SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector<unsigned char>& rawNode, uint32 seq)
: SHAMapNode(id), mSeq(seq), mType(tnERROR), mFullBelow(false)
{
Serializer s(rawNode);
int type=s.removeLastByte();
int len=s.getLength();
if( (type<0) || (type>3) || (len<32) ) throw SHAMapException(InvalidNode);
if(type==0)
{ // transaction
mItem=boost::make_shared<SHAMapItem>(s.getSHA512Half(), s.peekData());
mType=tnTRANSACTION;
}
else if(type==1)
{ // account state
uint160 u;
s.get160(u, len-20);
s.chop(20);
if(u.isZero()) throw SHAMapException(InvalidNode);
mItem=boost::make_shared<SHAMapItem>(u, s.peekData());
mType=tnACCOUNT_STATE;
}
else if(type==2)
{ // full inner
if(len!=512) throw SHAMapException(InvalidNode);
for(int i=0; i<16; i++)
s.get256(mHashes[i], i*32);
mType=tnINNER;
}
else if(type==3)
{ // compressed inner
for(int i=0; i<(len/33); i++)
{
int pos;
s.get8(pos, 32+(i*33));
if( (pos<0) || (pos>=16)) throw SHAMapException(InvalidNode);
s.get256(mHashes[pos], i*33);
}
mType=tnINNER;
}
updateHash();
}
void SHAMapTreeNode::addRaw(Serializer &s)
{
if(mType==tnERROR) throw SHAMapException(InvalidNode);
if(mType==tnTRANSACTION)
{
mItem->addRaw(s);
s.add8(0);
assert(s.getLength()>32);
return;
}
if(mType==tnACCOUNT_STATE)
{
mItem->addRaw(s);
s.add160(mItem->getTag().to160());
s.add8(1);
return;
}
if(getBranchCount()<12)
{ // compressed node
for(int i=0; i<16; i++)
if(mHashes[i].isNonZero())
{
s.add256(mHashes[i]);
s.add8(i);
}
s.add8(3);
return;
}
for(int i=0; i<16; i++)
s.add256(mHashes[i]);
s.add8(2);
}
bool SHAMapTreeNode::updateHash()
{
uint256 nh;
if(mType==tnINNER)
{
bool empty=true;
for(int i=0; i<16; i++)
if(mHashes[i].isNonZero())
{
empty=false;
break;
}
if(!empty)
nh=Serializer::getSHA512Half(reinterpret_cast<unsigned char *>(mHashes), sizeof(mHashes));
}
else if(mType==tnACCOUNT_STATE)
{
Serializer s;
mItem->addRaw(s);
s.add160(mItem->getTag().to160());
nh=s.getSHA512Half();
}
else if(mType==tnTRANSACTION)
{
nh=Serializer::getSHA512Half(mItem->peekData());
}
else assert(false);
if(nh==mHash) return false;
mHash=nh;
return true;
}
bool SHAMapTreeNode::setItem(SHAMapItem::pointer& i, TNType type)
{
uint256 hash=getNodeHash();
mType=type;
mItem=i;
assert(isLeaf());
updateHash();
return getNodeHash()==hash;
}
SHAMapItem::pointer SHAMapTreeNode::getItem() const
{
assert(isLeaf());
return boost::make_shared<SHAMapItem>(*mItem);
}
int SHAMapTreeNode::getBranchCount() const
{
assert(isInner());
int ret=0;
for(int i=0; i<16; ++i)
if(mHashes[i].isNonZero()) ++ret;
return ret;
}
void SHAMapTreeNode::makeInner()
{
mItem=SHAMapItem::pointer();
memset(mHashes, 0, sizeof(mHashes));
mType=tnINNER;
mHash.zero();
}
void SHAMapTreeNode::dump()
{
std::cerr << "SHAMapTreeNode(" << getNodeID().GetHex() << ")" << std::endl;
}
std::string SHAMapTreeNode::getString() const
{
std::string ret="NodeID(";
ret+=boost::lexical_cast<std::string>(getDepth());
ret+=",";
ret+=getNodeID().GetHex();
ret+=")";
if(isInner())
{
for(int i=0; i<16; i++)
if(!isEmptyBranch(i))
{
ret+=",b";
ret+=boost::lexical_cast<std::string>(i);
}
}
if(isLeaf())
{
ret+=",leaf";
}
return ret;
}
bool SHAMapTreeNode::setChildHash(int m, const uint256 &hash)
{
assert( (m>=0) && (m<16) );
assert(mType==tnINNER);
if(mHashes[m]==hash)
return false;
mHashes[m]=hash;
return updateHash();
}
const uint256& SHAMapTreeNode::getChildHash(int m) const
{
assert( (m>=0) && (m<16) && (mType==tnINNER) );
return mHashes[m];
}

496
src/SHAMapSync.cpp Normal file
View File

@@ -0,0 +1,496 @@
#include <stack>
#include <boost/make_shared.hpp>
#include <openssl/rand.h>
#include "SHAMap.h"
void SHAMap::getMissingNodes(std::vector<SHAMapNode>& nodeIDs, std::vector<uint256>& hashes, int max)
{
boost::recursive_mutex::scoped_lock sl(mLock);
assert(root->isValid());
if(root->isFullBelow())
{
#ifdef GMN_DEBUG
std::cerr << "getMissingNodes: root is full below" << std::endl;
#endif
clearSynching();
return;
}
if(!root->isInner())
{
std::cerr << "synching empty tree" << std::endl;
return;
}
std::stack<SHAMapTreeNode::pointer> stack;
stack.push(root);
while( (max>0) && (!stack.empty()) )
{
SHAMapTreeNode::pointer node=stack.top();
stack.pop();
#ifdef GMN_DEBUG
std::cerr << "gMN: popped " << node->getString() << std::endl;
#endif
for(int i=0; i<16; i++)
if(!node->isEmptyBranch(i))
{
#ifdef GMN_DEBUG
std::cerr << "gMN: " << node->getString() << " has non-empty branch " << i << std::endl;
#endif
SHAMapTreeNode::pointer desc=getNode(node->getChildNodeID(i), node->getChildHash(i), false);
if(desc)
{
if(desc->isInner() && !desc->isFullBelow())
stack.push(desc);
}
else if(max-- > 0)
{
#ifdef GMN_DEBUG
std::cerr << "gMN: need " << node->getChildNodeID(i).getString() << std::endl;
#endif
nodeIDs.push_back(node->getChildNodeID(i));
}
}
}
}
bool SHAMap::getNodeFat(const SHAMapNode& wanted, std::vector<SHAMapNode>& nodeIDs,
std::list<std::vector<unsigned char> >& rawNodes)
{
boost::recursive_mutex::scoped_lock sl(mLock);
SHAMapTreeNode::pointer node=getNode(wanted);
if(!node)
{
assert(false); // Remove for release, this can happen if we get a bogus request
return false;
}
nodeIDs.push_back(*node);
Serializer s;
node->addRaw(s);
rawNodes.push_back(s.peekData());
if(node->isRoot() || node->isLeaf()) // don't get a fat root, can't get a fat leaf
return true;
for(int i=0; i<16; i++)
if(!node->isEmptyBranch(i))
{
SHAMapTreeNode::pointer nextNode=getNode(node->getChildNodeID(i), node->getChildHash(i), false);
assert(nextNode);
if(nextNode)
{
nodeIDs.push_back(*nextNode);
Serializer s;
nextNode->addRaw(s);
rawNodes.push_back(s.peekData());
}
}
return true;
}
bool SHAMap::addRootNode(const std::vector<unsigned char>& rootNode)
{
boost::recursive_mutex::scoped_lock sl(mLock);
// we already have a root node
if(root->getNodeHash().isNonZero())
{
#ifdef DEBUG
std::cerr << "got root node, already have one" << std::endl;
#endif
return true;
}
SHAMapTreeNode::pointer node=boost::make_shared<SHAMapTreeNode>(SHAMapNode(), rootNode, 0);
if(!node) return false;
#ifdef DEBUG
node->dump();
#endif
returnNode(root, true);
root=node;
mTNByID[*root]=root;
if(!root->getNodeHash())
{
root->setFullBelow();
clearSynching();
}
return true;
}
bool SHAMap::addRootNode(const uint256& hash, const std::vector<unsigned char>& rootNode)
{
boost::recursive_mutex::scoped_lock sl(mLock);
// we already have a root node
if(root->getNodeHash().isNonZero())
{
#ifdef DEBUG
std::cerr << "got root node, already have one" << std::endl;
#endif
assert(root->getNodeHash()==hash);
return true;
}
SHAMapTreeNode::pointer node=boost::make_shared<SHAMapTreeNode>(SHAMapNode(), rootNode, 0);
if(!node) return false;
if(node->getNodeHash()!=hash) return false;
returnNode(root, true);
root=node;
mTNByID[*root]=root;
if(!root->getNodeHash())
{
root->setFullBelow();
clearSynching();
}
return true;
}
bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vector<unsigned char>& rawNode)
{ // return value: true=okay, false=error
assert(!node.isRoot());
if(!isSynching()) return false;
boost::recursive_mutex::scoped_lock sl(mLock);
if(checkCacheNode(node)) return true;
std::stack<SHAMapTreeNode::pointer> stack=getStack(node.getNodeID(), true);
if(stack.empty()) return false;
SHAMapTreeNode::pointer iNode=stack.top();
if(!iNode)
{ // we should always have a root
assert(false);
return true;
}
if(iNode->isLeaf() || (iNode->getDepth()==node.getDepth()))
{
#ifdef DEBUG
std::cerr << "got inner node, already had it (late)" << std::endl;
#endif
return true;
}
if(iNode->getDepth()!=(node.getDepth()-1))
{ // Either this node is broken or we didn't request it
#ifdef DEBUG
std::cerr << "unable to hook node " << node.getString() << std::endl;
std::cerr << " stuck at " << iNode->getString() << std::endl;
std::cerr << "got depth=" << node.getDepth() << ", walked to= " << iNode->getDepth() << std::endl;
#endif
return false;
}
int branch=iNode->selectBranch(node.getNodeID());
if(branch<0)
{
assert(false);
return false;
}
uint256 hash=iNode->getChildHash(branch);
if(!hash) return false;
SHAMapTreeNode::pointer newNode=boost::make_shared<SHAMapTreeNode>(node, rawNode, mSeq);
if(hash!=newNode->getNodeHash()) // these aren't the droids we're looking for
return false;
mTNByID[*newNode]=newNode;
if(!newNode->isLeaf())
return true; // only a leaf can fill a branch
// did this new leaf cause its parents to fill up
do
{
iNode=stack.top();
stack.pop();
assert(iNode->isInner());
for(int i=0; i<16; i++)
if(!iNode->isEmptyBranch(i))
{
SHAMapTreeNode::pointer nextNode=getNode(iNode->getChildNodeID(i), iNode->getChildHash(i), false);
if(!nextNode) return true;
if(nextNode->isInner() && !nextNode->isFullBelow()) return true;
}
iNode->setFullBelow();
} while(!stack.empty());
if(root->isFullBelow()) clearSynching();
return true;
}
bool SHAMap::deepCompare(SHAMap& other)
{ // Intended for debug/test only
std::stack<SHAMapTreeNode::pointer> stack;
boost::recursive_mutex::scoped_lock sl(mLock);
stack.push(root);
while(!stack.empty())
{
SHAMapTreeNode::pointer node=stack.top();
stack.pop();
SHAMapTreeNode::pointer otherNode;
if(node->isRoot()) otherNode=other.root;
else otherNode=other.getNode(*node, node->getNodeHash(), false);
if(!otherNode)
{
std::cerr << "unable to fetch node" << std::endl;
return false;
}
else if(otherNode->getNodeHash()!=node->getNodeHash())
{
std::cerr << "node hash mismatch" << std::endl;
return false;
}
#ifdef DC_DEBUG
std::cerr << "Comparing inner nodes " << node->getString() << std::endl;
#endif
if(node->getNodeHash() != otherNode->getNodeHash())
return false;
if(node->isLeaf())
{
if(!otherNode->isLeaf())
return false;
if(node->peekItem()->getTag()!=otherNode->peekItem()->getTag())
return false;
if(node->peekItem()->getData()!=otherNode->peekItem()->getData())
return false;
}
else if(node->isInner())
{
if(!otherNode->isInner())
return false;
for(int i=0; i<16; i++)
{
if(node->isEmptyBranch(i))
{
if(!otherNode->isEmptyBranch(i))
return false;
}
else
{
SHAMapTreeNode::pointer next=getNode(node->getChildNodeID(i), node->getChildHash(i), false);
if(!next)
{
std::cerr << "unable to fetch inner node" << std::endl;
return false;
}
stack.push(next);
}
}
}
}
return true;
}
#ifdef DEBUG
#define SMS_DEBUG
#endif
static SHAMapItem::pointer makeRandomAS()
{
Serializer s;
for(int d=0; d<3; d++)
s.add32(rand());
return boost::make_shared<SHAMapItem>(s.getRIPEMD160(), s.peekData());
}
static bool confuseMap(SHAMap &map, int count)
{
// add a bunch of random states to a map, then remove them
// map should be the same
uint256 beforeHash=map.getHash();
std::list<uint256> items;
for(int i=0; i<count; i++)
{
SHAMapItem::pointer item=makeRandomAS();
items.push_back(item->getTag());
if(!map.addItem(*item, false))
{
std::cerr << "Unable to add item to map" << std::endl;
return false;
}
}
for(std::list<uint256>::iterator it=items.begin(); it!=items.end(); ++it)
{
if(!map.delItem(*it))
{
std::cerr << "Unable to remove item from map" << std::endl;
return false;
}
}
if(beforeHash!=map.getHash())
{
std::cerr << "Hashes do not match" << std::endl;
return false;
}
return true;
}
bool SHAMap::syncTest()
{
unsigned int seed;
RAND_pseudo_bytes(reinterpret_cast<unsigned char *>(&seed), sizeof(seed));
srand(seed);
SHAMap source, destination;
// add random data to the source map
int items=10000;
for(int i=0; i<items; i++)
source.addItem(*makeRandomAS(), false);
#ifdef DEBUG
std::cerr << "Adding items, then removing them" << std::endl;
#endif
if(!confuseMap(source, 500))
return false;
source.setImmutable();
#ifdef DEBUG
std::cerr << "SOURCE COMPLETE, SYNCHING" << std::endl;
#endif
std::vector<SHAMapNode> nodeIDs, gotNodeIDs;
std::list<std::vector<unsigned char> > gotNodes;
std::vector<uint256> hashes;
std::vector<SHAMapNode>::iterator nodeIDIterator;
std::list<std::vector<unsigned char> >::iterator rawNodeIterator;
int passes=0;
int nodes=0;
destination.setSynching();
if(!source.getNodeFat(SHAMapNode(), nodeIDs, gotNodes))
{
std::cerr << "GetNodeFat(root) fails" << std::endl;
assert(false);
return false;
}
if(gotNodes.size()!=1)
{
std::cerr << "Didn't get root node " << gotNodes.size() << std::endl;
assert(false);
return false;
}
if(!destination.addRootNode(*gotNodes.begin()))
{
std::cerr << "AddRootNode fails" << std::endl;
assert(false);
return false;
}
nodeIDs.clear();
gotNodes.clear();
#ifdef DEBUG
std::cerr << "ROOT COMPLETE, INNER SYNCHING" << std::endl;
#endif
#ifdef SMS_DEBUG
int bytes=0;
#endif
do
{
passes++;
hashes.clear();
// get the list of nodes we know we need
destination.getMissingNodes(nodeIDs, hashes, 2048);
if(nodeIDs.empty()) break;
#ifdef SMS_DEBUG
std::cerr << nodeIDs.size() << " needed nodes" << std::endl;
#endif
// get as many nodes as possible based on this information
for(nodeIDIterator=nodeIDs.begin(); nodeIDIterator!=nodeIDs.end(); ++nodeIDIterator)
if(!source.getNodeFat(*nodeIDIterator, gotNodeIDs, gotNodes))
{
std::cerr << "GetNodeFat fails" << std::endl;
assert(false);
return false;
}
assert(gotNodeIDs.size() == gotNodes.size());
nodeIDs.clear();
hashes.clear();
if(gotNodeIDs.empty())
{
std::cerr << "No nodes gotten" << std::endl;
assert(false);
return false;
}
#ifdef SMS_DEBUG
std::cerr << gotNodeIDs.size() << " found nodes" << std::endl;
#endif
for(nodeIDIterator=gotNodeIDs.begin(), rawNodeIterator=gotNodes.begin();
nodeIDIterator!=gotNodeIDs.end(); ++nodeIDIterator, ++rawNodeIterator)
{
nodes++;
#ifdef SMS_DEBUG
bytes+=rawNodeIterator->size();
#endif
if(!destination.addKnownNode(*nodeIDIterator, *rawNodeIterator))
{
std::cerr << "AddKnownNode fails" << std::endl;
assert(false);
return false;
}
}
gotNodeIDs.clear();
gotNodes.clear();
} while(1);
destination.clearSynching();
#ifdef SMS_DEBUG
std::cerr << "SYNCHING COMPLETE " << items << " items, " << nodes << " nodes, " <<
bytes/1024 << " KB" << std::endl;
#endif
if(!source.deepCompare(destination))
{
std::cerr << "DeepCompare fails" << std::endl;
assert(false);
return false;
}
#ifdef SMS_DEBUG
std::cerr << "SHAMapSync test passed: " << items << " items, " <<
passes << " passes, " << nodes << " nodes" << std::endl;
#endif
return true;
}

27
src/ScopedLock.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef __SCOPEDLOCKHOLDER__
#define __SCOPEDLOCKHOLDER__
#include <boost/thread/recursive_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/shared_ptr.hpp>
class ScopedLock
{
protected:
mutable boost::shared_ptr<boost::interprocess::scoped_lock<boost::recursive_mutex> > mHolder;
public:
ScopedLock(boost::recursive_mutex &mutex) :
mHolder(new boost::interprocess::scoped_lock<boost::recursive_mutex>(mutex))
{ ; }
void lock() const
{
mHolder->lock();
}
void unlock() const
{
mHolder->unlock();
}
};
#endif

43
src/SecureAllocator.h Normal file
View File

@@ -0,0 +1,43 @@
//
// Allocator that locks its contents from being paged
// out of memory and clears its contents before deletion.
//
template<typename T>
struct secure_allocator : public std::allocator<T>
{
// MSVC8 default copy constructor is broken
typedef std::allocator<T> base;
typedef typename base::size_type size_type;
typedef typename base::difference_type difference_type;
typedef typename base::pointer pointer;
typedef typename base::const_pointer const_pointer;
typedef typename base::reference reference;
typedef typename base::const_reference const_reference;
typedef typename base::value_type value_type;
secure_allocator() throw() {}
secure_allocator(const secure_allocator& a) throw() : base(a) {}
template <typename U>
secure_allocator(const secure_allocator<U>& a) throw() : base(a) {}
~secure_allocator() throw() {}
template<typename _Other> struct rebind
{ typedef secure_allocator<_Other> other; };
T* allocate(std::size_t n, const void *hint = 0)
{
T *p;
p = std::allocator<T>::allocate(n, hint);
if (p != NULL)
mlock(p, sizeof(T) * n);
return p;
}
void deallocate(T* p, std::size_t n)
{
if (p != NULL)
{
memset(p, 0, sizeof(T) * n);
munlock(p, sizeof(T) * n);
}
std::allocator<T>::deallocate(p, n);
}
};

235
src/Serializer.cpp Normal file
View File

@@ -0,0 +1,235 @@
#include "Serializer.h"
#include "key.h"
#include <cassert>
#include <openssl/ripemd.h>
#include <openssl/sha.h>
int Serializer::add16(uint16 i)
{
int ret=mData.size();
mData.push_back((unsigned char)(i>>8));
mData.push_back((unsigned char)(i&0xff));
return ret;
}
int Serializer::add32(uint32 i)
{
int ret=mData.size();
mData.push_back((unsigned char)(i>>24));
mData.push_back((unsigned char)((i>>16)&0xff));
mData.push_back((unsigned char)((i>>8)&0xff));
mData.push_back((unsigned char)(i&0xff));
return ret;
}
int Serializer::add64(uint64 i)
{
int ret=mData.size();
mData.push_back((unsigned char)(i>>56));
mData.push_back((unsigned char)((i>>48)&0xff));
mData.push_back((unsigned char)((i>>40)&0xff));
mData.push_back((unsigned char)((i>>32)&0xff));
mData.push_back((unsigned char)((i>>24)&0xff));
mData.push_back((unsigned char)((i>>16)&0xff));
mData.push_back((unsigned char)((i>>8)&0xff));
mData.push_back((unsigned char)(i&0xff));
return ret;
}
int Serializer::add160(const uint160& i)
{
int ret=mData.size();
mData.insert(mData.end(), i.begin(), i.end());
return ret;
}
int Serializer::add256(const uint256& i)
{
int ret=mData.size();
mData.insert(mData.end(), i.begin(), i.end());
return ret;
}
int Serializer::addRaw(const std::vector<unsigned char> &vector)
{
int ret=mData.size();
mData.insert(mData.end(), vector.begin(), vector.end());
return ret;
}
int Serializer::addRaw(const void *ptr, int len)
{
int ret=mData.size();
mData.insert(mData.end(), (const char *) ptr, ((const char *)ptr)+len);
return ret;
}
bool Serializer::get16(uint16& o, int offset) const
{
if((offset+2)>mData.size()) return false;
o=mData.at(offset++);
o<<=8; o|=mData.at(offset);
return true;
}
bool Serializer::get32(uint32& o, int offset) const
{
if((offset+4)>mData.size()) return false;
o=mData.at(offset++);
o<<=8; o|=mData.at(offset++); o<<=8; o|=mData.at(offset++);
o<<=8; o|=mData.at(offset);
return true;
}
bool Serializer::get64(uint64& o, int offset) const
{
if((offset+8)>mData.size()) return false;
o=mData.at(offset++);
o<<=8; o|=mData.at(offset++); o<<=8; o|=mData.at(offset++);
o<<=8; o|=mData.at(offset++); o<<=8; o|=mData.at(offset++);
o<<=8; o|=mData.at(offset++); o<<=8; o|=mData.at(offset++);
o<<=8; o|=mData.at(offset);
return true;
}
bool Serializer::get160(uint160& o, int offset) const
{
if((offset+20)>mData.size()) return false;
memcpy(&o, &(mData.front())+offset, 20);
return true;
}
bool Serializer::get256(uint256& o, int offset) const
{
if((offset+32)>mData.size()) return false;
memcpy(&o, &(mData.front())+offset, 32);
return true;
}
uint256 Serializer::get256(int offset) const
{
uint256 ret;
if((offset+32)>mData.size()) return ret;
memcpy(&ret, &(mData.front())+offset, 32);
return ret;
}
int Serializer::add8(unsigned char byte)
{
int ret=mData.size();
mData.push_back(byte);
return ret;
}
bool Serializer::get8(int& byte, int offset) const
{
if(offset>=mData.size()) return false;
byte=mData[offset];
return true;
}
bool Serializer::chop(int bytes)
{
if(bytes>mData.size()) return false;
mData.resize(mData.size()-bytes);
return true;
}
int Serializer::removeLastByte()
{
int size=mData.size()-1;
if(size<0)
{
assert(false);
return -1;
}
int ret=mData[size];
mData.resize(size);
return ret;
}
bool Serializer::getRaw(std::vector<unsigned char>& o, int offset, int length) const
{
if((offset+length)>mData.size()) return false;
o.assign(mData.begin()+offset, mData.begin()+offset+length);
return true;
}
std::vector<unsigned char> Serializer::getRaw(int offset, int length) const
{
std::vector<unsigned char> o;
if((offset+length)>mData.size()) return o;
o.assign(mData.begin()+offset, mData.begin()+offset+length);
return o;
}
uint160 Serializer::getRIPEMD160(int size) const
{
uint160 ret;
if((size<0)||(size>mData.size())) size=mData.size();
RIPEMD160(&(mData.front()), size, (unsigned char *) &ret);
return ret;
}
uint256 Serializer::getSHA256(int size) const
{
uint256 ret;
if((size<0)||(size>mData.size())) size=mData.size();
SHA256(&(mData.front()), size, (unsigned char *) &ret);
return ret;
}
uint256 Serializer::getSHA512Half(int size) const
{
return getSHA512Half(mData, size);
}
uint256 Serializer::getSHA512Half(const std::vector<unsigned char>& data, int size)
{
uint256 j[2];
if((size<0)||(size>data.size())) size=data.size();
SHA512(&(data.front()), size, (unsigned char *) j);
return j[0];
}
uint256 Serializer::getSHA512Half(const unsigned char *data, int len)
{
uint256 j[2];
SHA512(data, len, (unsigned char *) j);
return j[0];
}
bool Serializer::checkSignature(int pubkeyOffset, int signatureOffset) const
{
std::vector<unsigned char> pubkey, signature;
if(!getRaw(pubkey, pubkeyOffset, 65)) return false;
if(!getRaw(signature, signatureOffset, 72)) return false;
CKey pubCKey;
if(!pubCKey.SetPubKey(pubkey)) return false;
return pubCKey.Verify(getSHA512Half(signatureOffset), signature);
}
bool Serializer::checkSignature(const std::vector<unsigned char> &signature, CKey& key) const
{
return key.Verify(getSHA512Half(), signature);
}
bool Serializer::makeSignature(std::vector<unsigned char> &signature, CKey& key) const
{
return key.Sign(getSHA512Half(), signature);
}
bool Serializer::addSignature(CKey& key)
{
std::vector<unsigned char> signature;
if(!key.Sign(getSHA512Half(), signature)) return false;
assert(signature.size()==72);
addRaw(signature);
return true;
}
void Serializer::TestSerializer()
{
Serializer s(64);
}

75
src/Serializer.h Normal file
View File

@@ -0,0 +1,75 @@
#ifndef __SERIALIZER__
#define __SERIALIZER__
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>
#include "key.h"
#include "uint256.h"
class Serializer
{
public:
typedef boost::shared_ptr<Serializer> pointer;
protected:
std::vector<unsigned char> mData;
public:
Serializer(int n=256) { mData.reserve(n); }
Serializer(const std::vector<unsigned char> &data) : mData(data) { ; }
Serializer(const std::string& data) : mData(data.data(), (data.data()) + data.size()) { ; }
// assemble functions
int add8(unsigned char byte);
int add16(uint16);
int add32(uint32); // ledger indexes, account sequence
int add64(uint64); // timestamps, amounts
int add160(const uint160&); // account names, hankos
int add256(const uint256&); // transaction and ledger hashes
int addRaw(const std::vector<unsigned char> &vector);
int addRaw(const void *ptr, int len);
// disassemble functions
bool get8(int&, int offset) const;
bool get8(unsigned char&, int offset) const;
bool get16(uint16&, int offset) const;
bool get32(uint32&, int offset) const;
bool get64(uint64&, int offset) const;
bool get160(uint160&, int offset) const;
bool get256(uint256&, int offset) const;
uint256 get256(int offset) const;
bool getRaw(std::vector<unsigned char>&, int offset, int length) const;
std::vector<unsigned char> getRaw(int offset, int length) const;
// hash functions
uint160 getRIPEMD160(int size=-1) const;
uint256 getSHA256(int size=-1) const;
uint256 getSHA512Half(int size=-1) const;
static uint256 getSHA512Half(const std::vector<unsigned char>& data, int size=-1);
static uint256 getSHA512Half(const unsigned char *data, int len);
// totality functions
int getLength() const { return mData.size(); }
const void* getDataPtr() const { return &mData.front(); }
void* getDataPtr() { return &mData.front(); }
const std::vector<unsigned char>& peekData() const { return mData; }
std::vector<unsigned char> getData() const { return mData; }
std::string getString() const { return std::string(static_cast<const char *>(getDataPtr()), getLength()); }
void secureErase() { memset(&(mData.front()), 0, mData.size()); erase(); }
void erase() { mData.clear(); }
int removeLastByte();
bool chop(int num);
// signature functions
bool checkSignature(int pubkeyOffset, int signatureOffset) const;
bool checkSignature(const std::vector<unsigned char>& signature, CKey& rkey) const;
bool makeSignature(std::vector<unsigned char>& signature, CKey& rkey) const;
bool addSignature(CKey& rkey);
static void TestSerializer();
};
#endif

201
src/TaggedCache.h Normal file
View File

@@ -0,0 +1,201 @@
#ifndef __TAGGEDCACHE__
#define __TAGGEDCACHE__
#include <map>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/shared_ptr.hpp>
// This class implemented a cache and a map. The cache keeps objects alive
// in the map. The map allows multiple code paths that reference objects
// with the same tag to get the same actual object.
template <typename c_Key, typename c_Data> class TaggedCache
{
public:
typedef c_Key key_type;
typedef c_Data data_type;
typedef boost::weak_ptr<data_type> weak_data_ptr;
typedef boost::shared_ptr<data_type> data_ptr;
typedef std::pair<time_t, data_ptr> cache_entry;
protected:
mutable boost::recursive_mutex mLock;
int mTargetSize, mTargetAge;
std::map<key_type, cache_entry> mCache; // Hold strong reference to recent objects
time_t mLastSweep;
std::map<key_type, weak_data_ptr> mMap; // Track stored objects
public:
TaggedCache(int size, int age) : mTargetSize(size), mTargetAge(age), mLastSweep(time(NULL)) { ; }
int getTargetSize() const;
int getTargetAge() const;
int getCacheSize();
int getSweepAge();
void setTargetSize(int size);
void setTargetAge(int age);
void sweep();
bool touch(const key_type& key);
bool del(const key_type& key);
bool canonicalize(const key_type& key, boost::shared_ptr<c_Data>& data);
boost::shared_ptr<c_Data> fetch(const key_type& key);
boost::recursive_mutex& peekMutex() { return mLock; }
};
template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getTargetSize() const
{
mLock.lock();
int ret=mTargetSize;
mLock.unlock();
return ret;
}
template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getTargetAge() const
{
mLock.lock();
int ret=mTargetAge;
mLock.unlock();
return ret;
}
template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getCacheSize()
{
boost::recursive_mutex::scoped_lock sl(mLock);
return mCache.size();
}
template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::sweep()
{
boost::recursive_mutex::scoped_lock sl(mLock);
if(mCache.size()<mTargetSize) return;
time_t now=time(NULL);
if((mLastSweep+10)<now) return;
mLastSweep=now;
time_t target=now-mTargetAge;
// Pass 1, remove old objects from cache
typename std::map<key_type, cache_entry>::iterator cit=mCache.begin();
while(cit!=mCache.end())
{
if(cit->second->second.first<target)
mCache.erase(cit++);
else ++cit;
}
// Pass 2, remove dead objects from map
typename std::map<key_type, weak_data_ptr>::iterator mit=mMap.begin();
while(mit!=mMap.end())
{
if(mit->second->expired())
mMap.erase(mit++);
else ++mit;
}
}
template<typename c_Key, typename c_Data> bool TaggedCache<c_Key, c_Data>::touch(const key_type& key)
{ // If present, make current in cache
boost::recursive_mutex::scoped_lock sl(mLock);
// Is the object in the map?
typename std::map<key_type, weak_data_ptr>::iterator mit=mMap.find(key);
if(mit==mMap.end()) return false;
if(mit->second->expired())
{ // in map, but expired
mMap.erase(mit);
return false;
}
// Is the object in the cache?
typename std::map<key_type, cache_entry>::iterator cit=mCache.find(key);
if(cit!=mCache.end())
{ // in both map and cache
cit->second->first=time(NULL);
return true;
}
// In map but not cache, put in cache
mCache.insert(std::make_pair(key, std::make_pair(time(NULL), weak_data_ptr(cit->second->second))));
return true;
}
template<typename c_Key, typename c_Data> bool TaggedCache<c_Key, c_Data>::del(const key_type& key)
{ // Remove from cache, map unaffected
boost::recursive_mutex::scoped_lock sl(mLock);
typename std::map<key_type, cache_entry>::iterator cit=mCache.find(key);
if(cit==mCache.end()) return false;
mCache.erase(cit);
return true;
}
template<typename c_Key, typename c_Data>
bool TaggedCache<c_Key, c_Data>::canonicalize(const key_type& key, boost::shared_ptr<c_Data>& data)
{ // Return canonical value, store if needed, refresh in cache
// Return values: true=we had the data already
boost::recursive_mutex::scoped_lock sl(mLock);
typename std::map<key_type, weak_data_ptr>::iterator mit=mMap.find(key);
if(mit==mMap.end())
{ // not in map
mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data)));
mMap.insert(std::make_pair(key, data));
return false;
}
if(mit->second.expired())
{ // in map, but expired
mit->second=data;
mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data)));
return false;
}
data=mit->second.lock();
assert(!!data);
// Valid in map, is it in cache?
typename std::map<key_type, cache_entry>::iterator cit=mCache.find(key);
if(cit!=mCache.end())
cit->second.first=time(NULL); // Yes, refesh
else // no, add to cache
mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data)));
return true;
}
template<typename c_Key, typename c_Data>
boost::shared_ptr<c_Data> TaggedCache<c_Key, c_Data>::fetch(const key_type& key)
{
boost::recursive_mutex::scoped_lock sl(mLock);
// Is it in the map?
typename std::map<key_type, weak_data_ptr>::iterator mit=mMap.find(key);
if(mit==mMap.end()) return data_ptr(); // No, we're done
if(mit->second.expired())
{ // in map, but expired
mMap.erase(mit);
return data_ptr();
}
boost::shared_ptr<c_Data> data=mit->second.lock();
// Valid in map, is it in the cache?
typename std::map<key_type, cache_entry>::iterator cit=mCache.find(key);
if(cit!=mCache.end())
cit->second.first=time(NULL); // Yes, refresh
else // No, add to cache
mCache.insert(std::make_pair(key, std::make_pair(time(NULL), data)));
return data;
}
#endif

57
src/TimingService.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include "TimingService.h"
#include "Config.h"
#include "Application.h"
#include <iostream>
#include <ctime>
#include <boost/bind.hpp>
using namespace std;
using namespace boost;
/*
Only needs to start once we determine the network time
*/
TimingService::TimingService() : mLedgerTimer(NULL), mPropTimer(NULL), mValidTimer(NULL)
{
}
void TimingService::start(boost::asio::io_service& ioService)
{
// TODO: calculate the amount of seconds left in the current ledger
mLedgerTimer=new asio::deadline_timer(ioService, posix_time::seconds(theConfig.LEDGER_SECONDS)),
mLedgerTimer->async_wait(boost::bind(&TimingService::handleLedger, this));
mPropTimer=new asio::deadline_timer(ioService, posix_time::seconds(theConfig.LEDGER_PROPOSAL_DELAY_SECONDS));
}
void TimingService::handleLedger()
{
cout << "publish ledger" << endl;
#if 0
theApp->getLedgerMaster().startFinalization();
mLedgerTimer->expires_at(mLedgerTimer->expires_at() + boost::posix_time::seconds(theConfig.LEDGER_SECONDS));
mLedgerTimer->async_wait(boost::bind(&TimingService::handleLedger, this));
mPropTimer->expires_at(mLedgerTimer->expires_at() + boost::posix_time::seconds(theConfig.LEDGER_PROPOSAL_DELAY_SECONDS));
mPropTimer->async_wait(boost::bind(&TimingService::handleProp, this));
#endif
}
void TimingService::handleProp()
{
// theApp->getLedgerMaster().sendProposal();
}
void TimingService::handleValid()
{
// theApp->getLedgerMaster().endFinalization();
}
int TimingService::getCurrentLedgerIndex()
{
return( (time(NULL)-theConfig.NETWORK_START_TIME)/theConfig.LEDGER_SECONDS );
}

26
src/TimingService.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef __TIMINGSERVICE__
#define __TIMINGSERVICE__
#include <boost/asio.hpp>
/* responsible for keeping track of network time
and kicking off the publishing process
*/
class TimingService
{
boost::asio::deadline_timer* mLedgerTimer;
boost::asio::deadline_timer* mPropTimer;
boost::asio::deadline_timer* mValidTimer;
void handleLedger();
void handleProp();
void handleValid();
public:
TimingService();
void start(boost::asio::io_service& ioService);
static int getCurrentLedgerIndex();
};
#endif

385
src/Transaction.cpp Normal file
View File

@@ -0,0 +1,385 @@
#include <cassert>
#include "boost/lexical_cast.hpp"
#include "boost/make_shared.hpp"
#include "Application.h"
#include "Transaction.h"
#include "Wallet.h"
#include "BitcoinUtil.h"
#include "BinaryFormats.h"
using namespace std;
Transaction::Transaction() : mTransactionID(0), mAccountFrom(0), mAccountTo(0),
mAmount(0), mFee(0), mFromAccountSeq(0), mSourceLedger(0), mIdent(0),
mInLedger(0), mStatus(INVALID)
{
}
Transaction::Transaction(LocalAccount::pointer fromLocalAccount, const uint160& toAccount, uint64 amount,
uint32 ident, uint32 ledger) :
mAccountTo(toAccount), mAmount(amount), mSourceLedger(ledger), mIdent(ident), mInLedger(0), mStatus(NEW)
{
mAccountFrom=fromLocalAccount->getAddress();
mFromPubKey=fromLocalAccount->getPublicKey();
assert(mFromPubKey);
mFromAccountSeq=fromLocalAccount->getTxnSeq();
#ifdef DEBUG
std::cerr << "Construct local Txn" << std::endl;
std::cerr << "ledger(" << ledger << "), fromseq(" << mFromAccountSeq << ")" << std::endl;
#endif
if(!mFromAccountSeq)
{
#ifdef DEBUG
std::cerr << "Bad source account sequence" << std::endl;
assert(false);
#endif
mStatus=INCOMPLETE;
}
assert(mFromPubKey);
updateFee();
if(!sign(fromLocalAccount))
{
#ifdef DEBUG
std::cerr << "Unable to sign transaction" << std::endl;
#endif
mStatus=INCOMPLETE;
}
}
Transaction::Transaction(const std::vector<unsigned char> &t, bool validate) : mStatus(INVALID)
{
Serializer s(t);
if(s.getLength()<BTxSize) { assert(false); return; }
if(!s.get160(mAccountTo, BTxPDestAcct)) { assert(false); return; }
if(!s.get64(mAmount, BTxPAmount)) { assert(false); return; }
if(!s.get32(mFromAccountSeq, BTxPSASeq)) { assert(false); return; }
if(!s.get32(mSourceLedger, BTxPSLIdx)) { assert(false); return; }
if(!s.get32(mIdent, BTxPSTag)) { assert(false); return; }
if(!s.getRaw(mSignature, BTxPSig, BTxLSig)) { assert(false); return; }
std::vector<unsigned char> pubKey;
if(!s.getRaw(pubKey, BTxPSPubK, BTxLSPubK)) { assert(false); return; }
mFromPubKey=boost::make_shared<CKey>();
if(!mFromPubKey->SetPubKey(pubKey)) return;
mAccountFrom=Hash160(pubKey);
mFromPubKey=theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey);
updateID();
updateFee();
if(!validate || checkSign())
mStatus=NEW;
}
bool Transaction::sign(LocalAccount::pointer fromLocalAccount)
{
CKey::pointer privateKey=fromLocalAccount->getPrivateKey();
if(!privateKey)
{
#ifdef DEBUG
std::cerr << "No private key for signing" << std::endl;
#endif
return false;
}
if( (mAmount==0) || (mSourceLedger==0) || (mAccountTo==0) )
{
#ifdef DEBUG
std::cerr << "Bad amount, source ledger, or destination" << std::endl;
#endif
assert(false);
return false;
}
if(mAccountFrom!=fromLocalAccount->getAddress())
{
#ifdef DEBUG
std::cerr << "Source mismatch" << std::endl;
#endif
assert(false);
return false;
}
if(!getRaw(true)->makeSignature(mSignature, *privateKey))
{
#ifdef DEBUG
std::cerr << "Failed to make signature" << std::endl;
#endif
assert(false);
return false;
}
assert(mSignature.size()==72);
updateID();
return true;
}
void Transaction::updateFee()
{ // for now, all transactions have a 1,000 unit fee
mFee=1000;
}
bool Transaction::checkSign() const
{
assert(mFromPubKey);
Serializer::pointer signBuf=getRaw(true);
return getRaw(true)->checkSignature(mSignature, *mFromPubKey);
}
Serializer::pointer Transaction::getRaw(bool prefix) const
{
Serializer::pointer ret=boost::make_shared<Serializer>(77);
if(prefix) ret->add32(0x54584e00u);
ret->add160(mAccountTo);
ret->add64(mAmount);
ret->add32(mFromAccountSeq);
ret->add32(mSourceLedger);
ret->add32(mIdent);
ret->addRaw(mFromPubKey->GetPubKey());
assert( (prefix&&(ret->getLength()==77)) || (!prefix&&(ret->getLength()==73)) );
return ret;
}
Serializer::pointer Transaction::getSigned() const
{
Serializer::pointer ret(getRaw(false));
ret->addRaw(mSignature);
assert(ret->getLength()==BTxSize);
return ret;
}
void Transaction::updateID()
{
mTransactionID=getSigned()->getSHA512Half();
}
void Transaction::setStatus(TransStatus ts, uint32 lseq)
{
mStatus=ts;
mInLedger=lseq;
}
void Transaction::saveTransaction(Transaction::pointer txn)
{
txn->save();
}
bool Transaction::save() const
{
if((mStatus==INVALID)||(mStatus==REMOVED)) return false;
std::string sql="INSERT INTO Transactions "
"(TransID,FromAcct,FromSeq,FromLedger,Identifier,ToAcct,Amount,Fee,FirstSeen,CommitSeq,Status, Signature)"
" VALUES ('";
sql.append(mTransactionID.GetHex());
sql.append("','");
sql.append(mAccountFrom.GetHex());
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mFromAccountSeq));
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mSourceLedger));
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mIdent));
sql.append("','");
sql.append(mAccountTo.GetHex());
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mAmount));
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mFee));
sql.append("',now(),'");
sql.append(boost::lexical_cast<std::string>(mInLedger)); switch(mStatus)
{
case NEW: sql.append("','N',"); break;
case INCLUDED: sql.append("','A',"); break;
case CONFLICTED: sql.append("','C',"); break;
case COMMITTED: sql.append("','D',"); break;
case HELD: sql.append("','H',"); break;
default: sql.append("','U',"); break;
}
std::string signature;
theApp->getTxnDB()->getDB()->escape(&(mSignature.front()), mSignature.size(), signature);
sql.append(signature);
sql.append(");");
ScopedLock sl(theApp->getTxnDB()->getDBLock());
Database* db=theApp->getTxnDB()->getDB();
return db->executeSQL(sql.c_str());
}
Transaction::pointer Transaction::transactionFromSQL(const std::string& sql)
{
std::string transID, fromID, toID, status;
uint64 amount, fee;
uint32 ledgerSeq, fromSeq, fromLedger, ident;
std::vector<unsigned char> signature;
signature.reserve(78);
if(1)
{
ScopedLock sl(theApp->getTxnDB()->getDBLock());
Database* db=theApp->getTxnDB()->getDB();
if(!db->executeSQL(sql.c_str(), true) || !db->startIterRows() || !db->getNextRow())
return Transaction::pointer();
db->getStr("TransID", transID);
db->getStr("FromID", fromID);
fromSeq=db->getBigInt("FromSeq");
fromLedger=db->getBigInt("FromLedger");
db->getStr("ToID", toID);
amount=db->getBigInt("Amount");
fee=db->getBigInt("Fee");
ledgerSeq=db->getBigInt("CommitSeq");
ident=db->getBigInt("Identifier");
db->getStr("Status", status);
int sigSize=db->getBinary("Signature", &(signature.front()), signature.size());
signature.resize(sigSize);
db->endIterRows();
}
uint256 trID;
uint160 frID, tID;
trID.SetHex(transID);
frID.SetHex(fromID);
tID.SetHex(toID);
CKey::pointer pubkey=theApp->getPubKeyCache().locate(frID);
if(!pubkey) return Transaction::pointer();
TransStatus st(INVALID);
switch(status[0])
{
case 'N': st=NEW; break;
case 'A': st=INCLUDED; break;
case 'C': st=CONFLICTED; break;
case 'D': st=COMMITTED; break;
case 'H': st=HELD; break;
}
return Transaction::pointer(new Transaction(trID, frID, tID, pubkey, amount, fee, fromSeq, fromLedger,
ident, signature, ledgerSeq, st));
}
Transaction::pointer Transaction::load(const uint256& id)
{
std::string sql="SELECT * FROM Transactions WHERE TransID='";
sql.append(id.GetHex());
sql.append("';");
return transactionFromSQL(sql);
}
Transaction::pointer Transaction::findFrom(const uint160& fromID, uint32 seq)
{
std::string sql="SELECT * FROM Transactions WHERE FromID='";
sql.append(fromID.GetHex());
sql.append("' AND FromSeq='");
sql.append(boost::lexical_cast<std::string>(seq));
sql.append("';");
return transactionFromSQL(sql);
}
bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedgerSeq,
bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::SHAMapDiff& inMap,
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap)
{ // convert a straight SHAMap payload difference to a transaction difference table
// return value: true=ledgers are valid, false=a ledger is invalid
std::map<uint256, std::pair<SHAMapItem::pointer, SHAMapItem::pointer> >::const_iterator it;
for(it=inMap.begin(); it!=inMap.end(); ++it)
{
const uint256& id=it->first;
const SHAMapItem::pointer& first=it->second.first;
const SHAMapItem::pointer& second=it->second.second;
Transaction::pointer firstTrans, secondTrans;
if(!!first)
{ // transaction in our table
firstTrans=boost::make_shared<Transaction>(first->getData(), checkFirstTransactions);
if( (firstTrans->getStatus()==INVALID) || (firstTrans->getID()!=id) )
{
firstTrans->setStatus(INVALID, firstLedgerSeq);
return false;
}
else firstTrans->setStatus(INCLUDED, firstLedgerSeq);
}
if(!!second)
{ // transaction in other table
secondTrans=boost::make_shared<Transaction>(second->getData(), checkSecondTransactions);
if( (secondTrans->getStatus()==INVALID) || (secondTrans->getID()!=id) )
{
secondTrans->setStatus(INVALID, secondLedgerSeq);
return false;
}
else secondTrans->setStatus(INCLUDED, secondLedgerSeq);
}
assert(firstTrans || secondTrans);
if(firstTrans && secondTrans && (firstTrans->getStatus()!=INVALID) && (secondTrans->getStatus()!=INVALID))
return false; // one or the other SHAMap is structurally invalid or a miracle has happened
outMap[id]=std::pair<Transaction::pointer, Transaction::pointer>(firstTrans, secondTrans);
}
return true;
}
static bool isHex(char j)
{
if((j>='0') && (j<='9')) return true;
if((j>='A') && (j<='F')) return true;
if((j>='a') && (j<='f')) return true;
return false;
}
bool Transaction::isHexTxID(const std::string& txid)
{
if(txid.size()!=64) return false;
for(int i=0; i<64; i++)
if(!isHex(txid[i])) return false;
return true;
}
Json::Value Transaction::getJson(bool decorate, bool paid, bool credited) const
{
Json::Value ret(Json::objectValue);
ret["TransactionID"]=mTransactionID.GetHex();
ret["Amount"]=boost::lexical_cast<std::string>(mAmount);
ret["Fee"]=boost::lexical_cast<std::string>(mFee);
if(mInLedger) ret["InLedger"]=mInLedger;
switch(mStatus)
{
case NEW: ret["Status"]="new"; break;
case INVALID: ret["Status"]="invalid"; break;
case INCLUDED: ret["Status"]="included"; break;
case CONFLICTED: ret["Status"]="conflicted"; break;
case COMMITTED: ret["Status"]="committed"; break;
case HELD: ret["Status"]="held"; break;
case REMOVED: ret["Status"]="removed"; break;
case OBSOLETE: ret["Status"]="obsolete"; break;
case INCOMPLETE: ret["Status"]="incomplete"; break;
default: ret["Status"]="unknown";
}
Json::Value source(Json::objectValue);
source["AccountID"]=NewcoinAddress(mAccountFrom).GetString();
source["AccountSeq"]=mFromAccountSeq;
source["Ledger"]=mSourceLedger;
if(!!mIdent) source["Identifier"]=mIdent;
Json::Value destination(Json::objectValue);
destination["AccountID"]=NewcoinAddress(mAccountTo).GetString();
if(decorate)
{
LocalAccount::pointer lac=theApp->getWallet().getLocalAccount(mAccountFrom);
if(!!lac) source=lac->getJson();
lac=theApp->getWallet().getLocalAccount(mAccountTo);
if(!!lac) destination=lac->getJson();
}
if(paid) source["Paid"]=true;
if(credited) destination["Credited"]=true;
ret["Source"]=source;
ret["Destination"]=destination;
return ret;
}

118
src/Transaction.h Normal file
View File

@@ -0,0 +1,118 @@
#ifndef __TRANSACTION__
#define __TRANSACTION__
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/cstdint.hpp>
#include "../json/value.h"
#include "key.h"
#include "uint256.h"
#include "../obj/src/newcoin.pb.h"
#include "Hanko.h"
#include "Serializer.h"
#include "SHAMap.h"
#include "LocalAccount.h"
/*
We could have made something that inherited from the protobuf transaction but this seemed simpler
*/
enum TransStatus
{
NEW =0, // just received / generated
INVALID =1, // no valid signature, insufficient funds
INCLUDED =2, // added to the current ledger
CONFLICTED =3, // losing to a conflicting transaction
COMMITTED =4, // known to be in a ledger
HELD =5, // not valid now, maybe later
REMOVED =6, // taken out of a ledger
OBSOLETE =7, // a compatible transaction has taken precedence
INCOMPLETE =8 // needs more signatures
};
class Transaction : public boost::enable_shared_from_this<Transaction>
{
public:
typedef boost::shared_ptr<Transaction> pointer;
static const uint32 TransSignMagic=0x54584E00; // "TXN"
private:
uint256 mTransactionID;
uint160 mAccountFrom, mAccountTo;
uint64 mAmount, mFee;
uint32 mFromAccountSeq, mSourceLedger, mIdent;
CKey::pointer mFromPubKey;
std::vector<unsigned char> mSignature;
uint32 mInLedger;
TransStatus mStatus;
public:
Transaction();
Transaction(const std::vector<unsigned char>& rawTransaction, bool validate);
Transaction(LocalAccount::pointer fromLocal, const uint160& to, uint64 amount, uint32 ident, uint32 ledger);
bool sign(LocalAccount::pointer fromLocalAccount);
bool checkSign() const;
void updateID();
void updateFee();
Serializer::pointer getRaw(bool prefix) const;
Serializer::pointer getSigned() const;
const uint256& getID() const { return mTransactionID; }
const uint160& getFromAccount() const { return mAccountFrom; }
const uint160& getToAccount() const { return mAccountTo; }
uint64 getAmount() const { return mAmount; }
uint64 getFee() const { return mFee; }
uint32 getFromAccountSeq() const { return mFromAccountSeq; }
uint32 getSourceLedger() const { return mSourceLedger; }
uint32 getIdent() const { return mIdent; }
const std::vector<unsigned char>& getSignature() const { return mSignature; }
uint32 getLedger() const { return mInLedger; }
TransStatus getStatus() const { return mStatus; }
void setStatus(TransStatus status, uint32 ledgerSeq);
void setStatus(TransStatus status) { mStatus=status; }
// database functions
static void saveTransaction(Transaction::pointer);
bool save() const;
static Transaction::pointer load(const uint256& id);
static Transaction::pointer findFrom(const uint160& fromID, uint32 seq);
// conversion function
static bool convertToTransactions(uint32 ourLedgerSeq, uint32 otherLedgerSeq,
bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::SHAMapDiff& inMap,
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap);
bool operator<(const Transaction&) const;
bool operator>(const Transaction&) const;
bool operator==(const Transaction&) const;
bool operator!=(const Transaction&) const;
bool operator<=(const Transaction&) const;
bool operator>=(const Transaction&) const;
Json::Value getJson(bool decorate, bool paid_local=false, bool credited_local=false) const;
static bool isHexTxID(const std::string&);
protected:
static Transaction::pointer transactionFromSQL(const std::string& statement);
Transaction(const uint256& transactionID, const uint160& accountFrom, const uint160& accountTo,
CKey::pointer key, uint64 amount, uint64 fee, uint32 fromAccountSeq, uint32 sourceLedger,
uint32 ident, const std::vector<unsigned char>& signature, uint32 inLedger, TransStatus status) :
mTransactionID(transactionID), mAccountFrom(accountFrom), mAccountTo(accountTo),
mAmount(amount), mFee(fee), mFromAccountSeq(fromAccountSeq), mSourceLedger(sourceLedger),
mIdent(ident), mFromPubKey(key), mSignature(signature), mInLedger(inLedger), mStatus(status)
{ ; }
};
#endif

40
src/TransactionMaster.cpp Normal file
View File

@@ -0,0 +1,40 @@
#include "boost/bind.hpp"
#include "Application.h"
#include "TransactionMaster.h"
#ifndef CACHED_TRANSACTION_NUM
#define CACHED_TRANSACTION_NUM 65536
#endif
#ifndef CACHED_TRANSACTION_AGE
#define CACHED_TRANSACTION_AGE 1800
#endif
TransactionMaster::TransactionMaster() : mCache(CACHED_TRANSACTION_NUM, CACHED_TRANSACTION_AGE)
{
;
}
Transaction::pointer TransactionMaster::fetch(const uint256& txnID, bool checkDisk)
{
Transaction::pointer txn=mCache.fetch(txnID);
if(!checkDisk || txn) return txn;
txn=Transaction::load(txnID);
if(!txn) return txn;
mCache.canonicalize(txnID, txn);
return txn;
}
bool TransactionMaster::canonicalize(Transaction::pointer& txn, bool may_be_new)
{
uint256 tid=txn->getID();
if(!tid) return false;
if(mCache.canonicalize(tid, txn)) return true;
if(may_be_new)
theApp->getIOService().post(boost::bind(&Transaction::saveTransaction, txn));
return false;
}

24
src/TransactionMaster.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef __TRANSACTIONMASTER__
#define __TRANSACTIONMASTER__
#include "Transaction.h"
#include "TaggedCache.h"
// Tracks all transactions in memory
class TransactionMaster
{
protected:
TaggedCache<uint256, Transaction> mCache;
public:
TransactionMaster();
Transaction::pointer fetch(const uint256&, bool checkDisk);
// return value: true = we had the transaction already
bool canonicalize(Transaction::pointer& txn, bool maybeNew);
};
#endif

82
src/UniqueNodeList.cpp Normal file
View File

@@ -0,0 +1,82 @@
#include "UniqueNodeList.h"
#include "Application.h"
#include "Conversion.h"
void UniqueNodeList::addNode(uint160& hanko, std::vector<unsigned char>& publicKey)
{
Database* db=theApp->getNetNodeDB()->getDB();
std::string sql="INSERT INTO UNL (Hanko,PubKey) values (";
std::string hashStr;
db->escape(hanko.begin(), hanko.GetSerializeSize(), hashStr);
sql.append(hashStr);
sql.append(",");
db->escape(&(publicKey[0]), publicKey.size(), hashStr);
sql.append(hashStr);
sql.append(")");
ScopedLock sl(theApp->getNetNodeDB()->getDBLock());
db->executeSQL(sql.c_str());
}
void UniqueNodeList::removeNode(uint160& hanko)
{
Database* db=theApp->getNetNodeDB()->getDB();
std::string sql="DELETE FROM UNL where hanko=";
std::string hashStr;
db->escape(hanko.begin(), hanko.GetSerializeSize(), hashStr);
sql.append(hashStr);
ScopedLock sl(theApp->getNetNodeDB()->getDBLock());
db->executeSQL(sql.c_str());
}
// 0- we don't care, 1- we care and is valid, 2-invalid signature
#if 0
int UniqueNodeList::checkValid(newcoin::Validation& valid)
{
Database* db=theApp->getNetNodeDB()->getDB();
std::string sql="SELECT pubkey from UNL where hanko=";
std::string hashStr;
db->escape((unsigned char*) &(valid.hanko()[0]),valid.hanko().size(),hashStr);
sql.append(hashStr);
ScopedLock sl(theApp->getNetNodeDB()->getDBLock());
if( db->executeSQL(sql.c_str()) )
{
if(db->startIterRows() && db->getNextRow())
{
//theApp->getDB()->getBytes();
// TODO: check that the public key makes the correct signature of the validation
db->endIterRows();
return(1);
}
else db->endIterRows();
}
return(0); // not on our list
}
#endif
void UniqueNodeList::dumpUNL(std::string& retStr)
{
Database* db=theApp->getNetNodeDB()->getDB();
std::string sql="SELECT * FROM UNL;";
ScopedLock sl(theApp->getNetNodeDB()->getDBLock());
if( db->executeSQL(sql.c_str()) )
{
db->startIterRows();
while(db->getNextRow())
{
uint160 hanko;
db->getBinary("Hanko", hanko.begin(), hanko.GetSerializeSize());
std::string tstr;
u160ToHuman(hanko, tstr);
retStr.append(tstr);
retStr.append("\n");
}
db->endIterRows();
}
}

25
src/UniqueNodeList.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef __UNIQUE_NODE_LIST__
#define __UNIQUE_NODE_LIST__
#include "../obj/src/newcoin.pb.h"
#include "uint256.h"
class UniqueNodeList
{
// hanko to public key
//std::map<uint160, uint256> mUNL;
public:
//void load();
//void save();
void addNode(uint160& hanko,std::vector<unsigned char>& publicKey);
void removeNode(uint160& hanko);
// 0- we don't care, 1- we care and is valid, 2-invalid signature
// int checkValid(newcoin::Validation& valid);
void dumpUNL(std::string& retStr);
};
#endif

View File

@@ -0,0 +1,234 @@
#include "ValidationCollection.h"
#include "Application.h"
#include "NewcoinAddress.h"
#include "Config.h"
#include "Conversion.h"
#include "Application.h"
#include <boost/foreach.hpp>
using namespace std;
/*
We need to group validations into compatible groups.
We can make one super ledger of all the transactions in each compatible group.
Then we just have to check this ledger to see if a new ledger is compatible
This is also the ledger we hand back when we ask for the consensus ledger
*/
ValidationCollection::ValidationCollection()
{
}
void ValidationCollection::save()
{
}
void ValidationCollection::load()
{
}
void ValidationCollection::addToDB(newcoin::Validation& valid,int weCare)
{
Database* db=theApp->getDB();
uint256 hash=protobufTo256(valid.hash());
uint160 hanko=protobufTo160(valid.hanko());
uint256 sig=protobufTo256(valid.sig());
string hashStr,hankoStr,sigStr;
db->escape(hash.begin(),hash.GetSerializeSize(),hashStr);
db->escape(hanko.begin(),hanko.GetSerializeSize(),hankoStr);
db->escape(sig.begin(),sig.GetSerializeSize(),sigStr);
string sql=strprintf("INSERT INTO Validations (LedgerIndex,Hash,Hanko,SeqNum,Sig,WeCare) values (%d,%s,%s,%d,%s,%d)",valid.ledgerindex(),hashStr.c_str(),hankoStr.c_str(),valid.seqnum(),sigStr.c_str(),weCare);
db->executeSQL(sql.c_str());
}
bool ValidationCollection::hasValidation(uint32 ledgerIndex,uint160& hanko,uint32 seqnum)
{
string hankoStr;
Database* db=theApp->getDB();
db->escape(hanko.begin(),hanko.GetSerializeSize(),hankoStr);
string sql=strprintf("SELECT ValidationID,seqnum from Validations where LedgerIndex=%d and hanko=%s",
ledgerIndex,hankoStr.c_str());
if(db->executeSQL(sql.c_str()))
{
if(db->startIterRows())
{
if(db->getNextRow())
{
uint32 currentSeqNum=db->getInt(1);
if(currentSeqNum>=seqnum)
{
db->endIterRows();
return(true);
}
// delete the old validation we were storing
sql=strprintf("DELETE FROM Validations where ValidationID=%d",db->getInt(0));
db->endIterRows();
db->executeSQL(sql.c_str());
}
}
}
return(false);
}
// TODO: we are adding our own validation
// TODO: when do we check if we are with the consensus?
// TODO: throw out lower seqnums
void ValidationCollection::addValidation(newcoin::Validation& valid)
{
// TODO: make sure the validation is valid
uint256 hash=protobufTo256(valid.hash());
uint160 hanko=protobufTo160(valid.hanko());
// make sure we don't already have this validation
if(hasValidation(valid.ledgerindex(),hanko,valid.seqnum())) return;
// check if we care about this hanko
int validity=theApp->getUNL().checkValid(valid);
if( validity==1 )
{
addToDB(valid,true);
addToGroup(valid);
theApp->getLedgerMaster().checkConsensus(valid.ledgerindex());
}else if(validity==0)
{
addToDB(valid,false);
}else
{ // the signature wasn't valid
cout << "Invalid Validation" << endl;
}
}
bool ValidationCollection::Group::addIfCompatible(Ledger::pointer ledger,newcoin::Validation& valid)
{
if(mSuperLedger)
{
if(mSuperLedger->isCompatible(ledger))
{
mValidations.push_back(valid);
mSuperLedger->mergeIn(ledger);
}
}
return(false);
}
// TODO: optimize. We can at least cache what ledgers are compatible
// a validation can be in multiple groups since compatibility isn't transitive
// Sometimes things are just complex
void ValidationCollection::addToGroup(newcoin::Validation& newValid)
{
if(mIndexGroups.count(newValid.ledgerindex()))
{
bool canReturn=false;
// see if this hash is already on the list. If so add it there.
vector< Group >& groups=mIndexGroups[newValid.ledgerindex()];
BOOST_FOREACH(Group& group,groups)
{
BOOST_FOREACH(newcoin::Validation& valid,group.mValidations)
{
if(valid.hash()==newValid.hash())
{
group.mValidations.push_back(newValid);
canReturn=true;
break;
}
}
}
if(canReturn) return;
// this is a validation of a new ledger hash
uint256 newHash=protobufTo256(newValid.hash());
Ledger::pointer newLedger=theApp->getLedgerMaster().getLedger(newHash);
if(newLedger)
{ // see if this ledger is compatible with any groups
bool foundGroup=false;
BOOST_FOREACH(Group& group,groups)
{
if(group.addIfCompatible(newLedger,newValid)) foundGroup=true;
}
if(!foundGroup)
{ // this validation didn't fit in any of the other groups
// we need to make a new group for it and see what validations fit it
Group& newGroup=mIndexGroups[newValid.ledgerindex()][groups.size()];
newGroup.mValidations.push_back(newValid);
newGroup.mSuperLedger=Ledger::pointer(new Ledger(newLedger)); // since this super ledger gets modified and we don't want to screw the original
vector<newcoin::Validation> retVec;
getValidations(newValid.ledgerindex(),retVec);
BOOST_FOREACH(newcoin::Validation& valid,retVec)
{
uint256 hash=protobufTo256(valid.hash());
Ledger::pointer ledger=theApp->getLedgerMaster().getLedger(hash);
newGroup.addIfCompatible(ledger,valid);
}
}
}else
{ // we don't have a ledger for this validation
// add to its own group since we can't check if it is compatible
int newIndex=groups.size();
mIndexGroups[newValid.ledgerindex()][newIndex].mValidations.push_back(newValid);
}
}else
{ // this is the first validation of this ledgerindex
uint256 newHash=protobufTo256(newValid.hash());
mIndexGroups[newValid.ledgerindex()][0].mValidations.push_back(newValid);
mIndexGroups[newValid.ledgerindex()][0].mSuperLedger=theApp->getLedgerMaster().getLedger(newHash);
}
}
void ValidationCollection::getValidations(uint32 ledgerIndex,vector<newcoin::Validation>& retVec)
{
string sql=strprintf("SELECT * From Validations where LedgerIndex=%d and wecare=1",ledgerIndex);
// TODO: ValidationCollection::getValidations(uint32 ledgerIndex)
}
// look through all the validated hashes at that index
// put the ledgers into compatible groups
// Pick the group with the most votes
bool ValidationCollection::getConsensusLedger(uint32 ledgerIndex, uint256& ourHash, Ledger::pointer& retLedger, uint256& retHash)
{
bool ret=false;
if(mIndexGroups.count(ledgerIndex))
{
unsigned int maxVotes=theConfig.MIN_VOTES_FOR_CONSENSUS;
vector< Group >& groups=mIndexGroups[ledgerIndex];
Group empty;
Group& maxGroup=empty;
BOOST_FOREACH(Group& group, groups)
{
if(group.mValidations.size()>maxVotes)
{
maxVotes=group.mValidations.size();
retLedger=group.mSuperLedger;
maxGroup=group;
if(!retLedger) retHash=protobufTo256(group.mValidations[0].hash());
ret=true;
}
}
if(ret)
{
// should also return false if we are in the consensus
BOOST_FOREACH(newcoin::Validation& valid, maxGroup.mValidations)
{
if(protobufTo256(valid.hash()) == ourHash) return(false);
}
}
}
return(ret);
}

View File

@@ -0,0 +1,56 @@
#ifndef __VALIDATION_COLLECTION__
#define __VALIDATION_COLLECTION__
#include "../obj/src/newcoin.pb.h"
#include "uint256.h"
#include "types.h"
#include "Ledger.h"
#include <list>
class ValidationCollection
{
// from ledger hash to the validation
//std::map<uint256, std::vector<newcoin::Validation> > mValidations;
//std::map<uint256, std::vector<newcoin::Validation> > mIgnoredValidations;
// this maps ledgerIndex to an array of groups. Each group is a list of validations.
// a validation can be in multiple groups since compatibility isn't transitive
//
class Group
{
public:
std::vector<newcoin::Validation> mValidations;
Ledger::pointer mSuperLedger;
bool addIfCompatible(Ledger::pointer ledger,newcoin::Validation& valid);
};
std::map<uint32, std::vector< Group > > mIndexGroups; // all the groups at each index
//std::map<uint32, std::vector< newcoin::Validation > > mIndexValidations; // all the validations at each index
bool hasValidation(uint32 ledgerIndex,uint160& hanko,uint32 seqnum);
void addToGroup(newcoin::Validation& valid);
void addToDB(newcoin::Validation& valid,int weCare);
public:
ValidationCollection();
void save();
void load();
void addValidation(newcoin::Validation& valid);
void getValidations(uint32 ledgerIndex,std::vector<newcoin::Validation>& retVec);
// It can miss some compatible ledgers of course if you don't know them
// fills out retLedger if there is a consensusLedger you can check
// fills out retHash if there isn't a consensusLedger to check. We need to fetch this ledger
// returns false if there isn't a consensus yet or we are in the consensus
bool getConsensusLedger(uint32 ledgerIndex, uint256& ourHash, Ledger::pointer& retLedger, uint256& retHash);
int getSeqNum(uint32 ledgerIndex);
};
#endif

914
src/Wallet.cpp Normal file
View File

@@ -0,0 +1,914 @@
#define WIN32_LEAN_AND_MEAN
#include <string>
#include "openssl/rand.h"
#include "openssl/ec.h"
#include "boost/foreach.hpp"
#include "boost/lexical_cast.hpp"
#include "boost/interprocess/sync/scoped_lock.hpp"
#include "boost/make_shared.hpp"
#include "Wallet.h"
#include "Ledger.h"
#include "NewcoinAddress.h"
#include "Application.h"
// TEMPORARY
#ifndef CHECK_NEW_FAMILIES
#define CHECK_NEW_FAMILIES 500
#endif
LocalAccount::LocalAccount(boost::shared_ptr<LocalAccountFamily> family, int familySeq) :
mPublicKey(family->getPublicKey(familySeq)), mFamily(family), mAccountFSeq(familySeq),
mLgrBalance(0), mTxnDelta(0), mTxnSeq(0)
{
mAcctID=mPublicKey->GetAddress().GetHash160();
if(theApp!=NULL) mPublicKey=theApp->getPubKeyCache().store(mAcctID, mPublicKey);
}
std::string LocalAccount::getAccountName() const
{
return mPublicKey->GetAddress().GetString();
}
std::string LocalAccount::getLocalAccountName() const
{
return NewcoinAddress(mFamily->getFamily()).GetString() + ":" + boost::lexical_cast<std::string>(mAccountFSeq);
}
LocalAccountFamily::LocalAccountFamily(const uint160& family, const EC_GROUP* group, const EC_POINT* pubKey) :
mFamily(family), mLastSeq(0), mRootPrivateKey(NULL)
{
mRootPubKey=EC_POINT_dup(pubKey, group);
mName=family.GetHex().substr(0, 4);
}
LocalAccountFamily::~LocalAccountFamily()
{
lock();
if(mRootPubKey!=NULL) EC_POINT_free(mRootPubKey);
}
uint160 LocalAccountFamily::getAccount(int seq, bool keep)
{
std::map<int, LocalAccount::pointer>::iterator ait=mAccounts.find(seq);
if(ait!=mAccounts.end()) return ait->second->getAddress();
LocalAccount::pointer lae=boost::make_shared<LocalAccount>(shared_from_this(), seq);
mAccounts.insert(std::make_pair(seq, lae));
return lae->getAddress();
}
void LocalAccountFamily::unlock(const BIGNUM* privateKey)
{
if(mRootPrivateKey==NULL) mRootPrivateKey=BN_dup(privateKey);
#ifdef CHECK_NEW_FAMILIES
std::cerr << "CheckingFamily" << std::endl;
for(int i=0; i<CHECK_NEW_FAMILIES; i++)
{
EC_KEY *pubkey=CKey::GeneratePublicDeterministicKey(mFamily, mRootPubKey, i);
EC_KEY *privkey=CKey::GeneratePrivateDeterministicKey(mFamily, mRootPrivateKey, i);
int cmp=EC_POINT_cmp(EC_KEY_get0_group(pubkey), EC_KEY_get0_public_key(pubkey),
EC_KEY_get0_public_key(privkey), NULL);
if(cmp!=0)
{
std::cerr << BN_bn2hex(mRootPrivateKey) << std::endl;
std::cerr << "family=" << mFamily.GetHex() << std::endl;
std::cerr << "i=" << i << std::endl;
std::cerr << "Key mismatch" << std::endl;
assert(false);
}
EC_KEY_free(pubkey);
EC_KEY_free(privkey);
}
#endif
}
void LocalAccountFamily::lock()
{
if(mRootPrivateKey!=NULL)
{
BN_free(mRootPrivateKey);
mRootPrivateKey=NULL;
}
}
CKey::pointer LocalAccountFamily::getPublicKey(int seq)
{
return boost::make_shared<CKey>(mFamily, mRootPubKey, seq);
}
CKey::pointer LocalAccountFamily::getPrivateKey(int seq)
{
if(!mRootPrivateKey) return CKey::pointer();
std::cerr << "PrivKey(" << mFamily.GetHex() << "," << seq << ")" << std::endl;
return boost::make_shared<CKey>(mFamily, mRootPrivateKey, seq);
}
std::string LocalAccountFamily::getPubGenHex() const
{
EC_GROUP *grp=EC_GROUP_new_by_curve_name(NID_secp256k1);
if(!grp) return "";
BIGNUM* pubBase=EC_POINT_point2bn(grp, mRootPubKey, POINT_CONVERSION_COMPRESSED, NULL, NULL);
EC_GROUP_free(grp);
if(!pubBase) return "";
char *hex=BN_bn2hex(pubBase);
BN_free(pubBase);
if(!hex) return "";
std::string ret(hex);
OPENSSL_free(hex);
return ret;
}
static bool isHex(char j)
{
if((j>='0') && (j<='9')) return true;
if((j>='A') && (j<='F')) return true;
if((j>='a') && (j<='f')) return true;
return false;
}
Json::Value LocalAccountFamily::getJson() const
{
Json::Value ret(Json::objectValue);
ret["ShortName"]=getShortName();
ret["FullName"]=getFamily().GetHex();
ret["PublicGenerator"]=getPubGenHex();
ret["IsLocked"]=isLocked();
if(!getComment().empty()) ret["Comment"]=getComment();
return ret;
}
LocalAccountFamily::pointer LocalAccountFamily::readFamily(const uint160& family)
{
std::string sql="SELECT * from LocalAcctFamilies WHERE FamilyName='";
sql.append(family.GetHex());
sql.append("';");
std::string rootPubKey, name, comment;
uint32 seq;
if(1)
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
Database *db=theApp->getWalletDB()->getDB();
if(!db->executeSQL(sql.c_str()) || !db->startIterRows() || !db->getNextRow())
return LocalAccountFamily::pointer();
db->getStr("RootPubKey", rootPubKey);
db->getStr("Name", name);
db->getStr("Comment", comment);
seq=db->getBigInt("Seq");
db->endIterRows();
}
EC_GROUP *grp=EC_GROUP_new_by_curve_name(NID_secp256k1);
if(!grp) return LocalAccountFamily::pointer();
EC_POINT *pubKey=EC_POINT_hex2point(grp, rootPubKey.c_str(), NULL, NULL);
if(!pubKey)
{
EC_GROUP_free(grp);
assert(false);
return LocalAccountFamily::pointer();
}
LocalAccountFamily::pointer fam=boost::make_shared<LocalAccountFamily>(family, grp, pubKey);
EC_GROUP_free(grp);
EC_POINT_free(pubKey);
fam->setName(name);
fam->setComment(comment);
fam->setSeq(seq);
return fam;
}
void LocalAccountFamily::write(bool is_new)
{
std::string sql="INSERT INTO LocalAcctFamilies (FamilyName,RootPubKey,Seq,Name,Comment) VALUES ('";
sql.append(mFamily.GetHex());
sql.append("','");
EC_GROUP* grp=EC_GROUP_new_by_curve_name(NID_secp256k1);
if(!grp) return;
char *rpk=EC_POINT_point2hex(grp, mRootPubKey, POINT_CONVERSION_COMPRESSED, NULL);
EC_GROUP_free(grp);
if(!rpk) return;
sql.append(rpk);
OPENSSL_free(rpk);
sql.append("','");
sql.append(boost::lexical_cast<std::string>(mLastSeq));
sql.append("',");
std::string f;
theApp->getWalletDB()->getDB()->escape((const unsigned char *) mName.c_str(), mName.size(), f);
sql.append(f);
sql.append(",");
theApp->getWalletDB()->getDB()->escape((const unsigned char *) mComment.c_str(), mComment.size(), f);
sql.append(f);
sql.append(");");
ScopedLock sl(theApp->getWalletDB()->getDBLock());
theApp->getWalletDB()->getDB()->executeSQL(sql.c_str());
}
bool Wallet::isHexPrivateKey(const std::string& s)
{ // 65 characters, first is 'P', rest are all legal hex
if(s.size()!=65) return false;
if(s[0]!='P') return false;
for(int i=1; i<65; i++)
if(!isHex(s[i])) return false;
return true;
}
bool Wallet::isHexPublicKey(const std::string& s)
{ // 66 characters, all legal hex, starts with '02' or '03'
if(s.size()!=66) return false;
if(s[0]!='0') return false;
if((s[1]!='2') && (s[1]!='3')) return false;
for(int i=3; i<66; i++)
if(!isHex(s[i])) return false;
return true;
}
bool Wallet::isHexFamily(const std::string& s)
{ // 64 characters, all legal hex
if(s.size()!=64) return false;
for(int i=0; i<64; i++)
if(!isHex(s[i])) return false;
return true;
}
std::string Wallet::privKeyToText(const uint256& privKey)
{
std::string ret("P");
ret.append(privKey.GetHex());
return ret;
}
uint256 Wallet::textToPrivKey(const std::string& privKey)
{
uint256 ret;
if((privKey.length()==65) && (privKey[0]=='P'))
ret.SetHex(privKey.c_str()+1);
return ret;
}
std::string LocalAccountFamily::getSQLFields()
{
return "(FamilyName,RootPubKey,Seq,Name,Comment)";
}
std::string LocalAccountFamily::getSQL() const
{ // familyname(40), pubkey(66), seq, name, comment
std::string ret("('");
ret.append(mFamily.GetHex());
ret+="','";
ret.append(getPubGenHex());
ret.append("','");
ret.append(boost::lexical_cast<std::string>(mLastSeq));
ret.append("',");
std::string esc;
theApp->getWalletDB()->getDB()->escape((const unsigned char *) mName.c_str(), mName.size(), esc);
ret.append(esc);
ret.append(",");
theApp->getWalletDB()->getDB()->escape((const unsigned char *) mComment.c_str(), mComment.size(), esc);
ret.append(esc);
ret.append(")");
return ret;
}
LocalAccount::pointer LocalAccountFamily::get(int seq)
{
std::map<int, LocalAccount::pointer>::iterator act=mAccounts.find(seq);
if(act!=mAccounts.end()) return act->second;
LocalAccount::pointer ret=boost::make_shared<LocalAccount>(shared_from_this(), seq);
mAccounts.insert(std::make_pair(seq, ret));
return ret;
}
uint160 Wallet::findFamilySN(const std::string& shortName)
{ // OPTIMIZEME
boost::recursive_mutex::scoped_lock sl(mLock);
for(std::map<uint160, LocalAccountFamily::pointer>::iterator it=mFamilies.begin();
it!=mFamilies.end(); ++it)
{
if(it->second->getShortName()==shortName)
return it->first;
}
return 0;
}
uint160 Wallet::addFamily(const uint256& key, bool lock)
{
LocalAccountFamily::pointer fam(doPrivate(key, true, !lock));
if(!fam) return uint160();
return fam->getFamily();
}
uint160 Wallet::addRandomFamily(uint256& key)
{
RAND_bytes((unsigned char *) &key, sizeof(key));
return addFamily(key, false);
}
uint160 Wallet::addFamily(const std::string& payPhrase, bool lock)
{
return addFamily(CKey::PassPhraseToKey(payPhrase), lock);
}
uint160 Wallet::addFamily(const std::string& pubKey)
{
LocalAccountFamily::pointer fam(doPublic(pubKey, true, true));
if(!fam) return uint160();
return fam->getFamily();
}
uint160 Wallet::findFamilyPK(const std::string& pubKey)
{
LocalAccountFamily::pointer fam(doPublic(pubKey, false, true));
if(!fam) return uint160();
return fam->getFamily();
}
std::string LocalAccount::getShortName() const
{
std::string ret, cmt;
if(!theApp->getWallet().getFamilyInfo(mFamily->getFamily(), ret, cmt))
ret=mFamily->getFamily().GetHex();
ret.append(":");
if(mName.empty())
ret.append(boost::lexical_cast<std::string>(mAccountFSeq));
else ret.append(mName);
return ret;
}
std::string LocalAccount::getFullName() const
{
std::string ret(mFamily->getFamily().GetHex());
ret.append(":");
ret.append(boost::lexical_cast<std::string>(mAccountFSeq));
return ret;
}
bool LocalAccount::isLocked() const
{
return mFamily->isLocked();
}
std::string LocalAccount::getFamilyName() const
{
return mFamily->getShortName();
}
Json::Value LocalAccount::getJson() const
{
Json::Value ret(Json::objectValue);
ret["Family"]=getFamilyName();
ret["AccountID"]=NewcoinAddress(getAddress()).GetString();
ret["ShortName"]=getShortName();
ret["FullName"]=getFullName();
ret["Issued"]=Json::Value(isIssued());
ret["IsLocked"]=mFamily->isLocked();
uint64 eb=getEffectiveBalance();
if(eb!=0) ret["Balance"]=boost::lexical_cast<std::string>(eb);
uint32 sq=getTxnSeq();
if(sq!=0) ret["TxnSeq"]=boost::lexical_cast<std::string>(sq);
return ret;
}
bool LocalAccount::isIssued() const
{
return mAccountFSeq < mFamily->getSeq();
}
CKey::pointer LocalAccount::getPrivateKey()
{
return mFamily->getPrivateKey(mAccountFSeq);
}
void Wallet::getFamilies(std::vector<uint160>& familyIDs)
{
boost::recursive_mutex::scoped_lock sl(mLock);
familyIDs.reserve(mFamilies.size());
for(std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.begin(); fit!=mFamilies.end(); ++fit)
familyIDs.push_back(fit->first);
}
bool Wallet::getFamilyInfo(const uint160& family, std::string& name, std::string& comment)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(family);
if(fit==mFamilies.end()) return false;
assert(fit->second->getFamily()==family);
name=fit->second->getShortName();
comment=fit->second->getComment();
return true;
}
bool Wallet::getFullFamilyInfo(const uint160& family, std::string& name, std::string& comment,
std::string& pubGen, bool& isLocked)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(family);
if(fit==mFamilies.end()) return false;
assert(fit->second->getFamily()==family);
name=fit->second->getShortName();
comment=fit->second->getComment();
pubGen=fit->second->getPubGenHex();
isLocked=fit->second->isLocked();
return true;
}
Json::Value Wallet::getFamilyJson(const uint160& family)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(family);
if(fit==mFamilies.end()) return Json::Value(Json::nullValue);
assert(fit->second->getFamily()==family);
return fit->second->getJson();
}
void Wallet::load()
{
std::string sql("SELECT * FROM LocalAcctFamilies;");
ScopedLock sl(theApp->getWalletDB()->getDBLock());
Database *db=theApp->getWalletDB()->getDB();
if(!db->executeSQL(sql.c_str()))
{
#ifdef DEBUG
std::cerr << "Unable to load wallet" << std::endl;
#endif
return;
}
if(!db->startIterRows()) return;
while(db->getNextRow())
{
std::string family, rootpub, name, comment;
db->getStr("FamilyName", family);
db->getStr("RootPubKey", rootpub);
db->getStr("Name", name);
db->getStr("Comment", comment);
int seq=db->getBigInt("Seq");
uint160 fb;
fb.SetHex(family);
LocalAccountFamily::pointer f(doPublic(rootpub, true, false));
if(f)
{
assert(f->getFamily()==fb);
f->setSeq(seq);
f->setName(name);
f->setComment(comment);
}
else assert(false);
}
db->endIterRows();
}
std::string Wallet::getPubGenHex(const uint160& famBase)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(famBase);
if(fit==mFamilies.end()) return "";
assert(fit->second->getFamily()==famBase);
return fit->second->getPubGenHex();
}
std::string Wallet::getShortName(const uint160& famBase)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(famBase);
if(fit==mFamilies.end()) return "";
assert(fit->second->getFamily()==famBase);
return fit->second->getShortName();
}
LocalAccount::pointer Wallet::getNewLocalAccount(const uint160& family)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(family);
if(fit==mFamilies.end()) return LocalAccount::pointer();
uint32 seq=fit->second->getSeq();
uint160 acct=fit->second->getAccount(seq, true);
fit->second->setSeq(seq+1); // FIXME: writeout new seq
std::map<uint160, LocalAccount::pointer>::iterator ait=mAccounts.find(acct);
if(ait!=mAccounts.end()) return ait->second;
LocalAccount::pointer lac=boost::make_shared<LocalAccount>(fit->second, seq);
mAccounts.insert(std::make_pair(acct, lac));
sl.unlock();
lac->syncLedger();
return lac;
}
LocalAccount::pointer Wallet::getLocalAccount(const uint160& family, int seq)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(family);
if(fit==mFamilies.end()) return LocalAccount::pointer();
uint160 acct=fit->second->getAccount(seq, true);
std::map<uint160, LocalAccount::pointer>::iterator ait=mAccounts.find(acct);
if(ait!=mAccounts.end()) return ait->second;
LocalAccount::pointer lac=boost::make_shared<LocalAccount>(fit->second, seq);
mAccounts.insert(std::make_pair(acct, lac));
sl.unlock();
lac->syncLedger();
return lac;
}
LocalAccount::pointer Wallet::getLocalAccount(const uint160& acctID)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccount::pointer>::iterator ait=mAccounts.find(acctID);
if(ait==mAccounts.end()) return LocalAccount::pointer();
return ait->second;
}
LocalAccount::pointer Wallet::findAccountForTransaction(uint64 amount)
{
boost::recursive_mutex::scoped_lock sl(mLock);
for(std::map<uint160, LocalAccount::pointer>::iterator it=mAccounts.begin(); it!=mAccounts.end(); ++it)
if(!it->second->isLocked() && (it->second->getEffectiveBalance()>=amount) )
return it->second;
return LocalAccount::pointer();
}
LocalAccount::pointer Wallet::parseAccount(const std::string& specifier)
{ // <family>:<seq> or <acct_id>
std::cerr << "Parse(" << specifier << ")" << std::endl;
size_t colon=specifier.find(':');
if(colon == std::string::npos)
{
std::cerr << "nocolon" << std::endl;
NewcoinAddress na(specifier);
if(!na.IsValid()) return LocalAccount::pointer();
return getLocalAccount(na.GetHash160());
}
if (colon==0) return LocalAccount::pointer();
std::string family=specifier.substr(0, colon);
std::string seq=specifier.substr(colon+1);
if(seq.empty()) return LocalAccount::pointer();
std::cerr << "family(" << family << "), seq(" << seq << ")" << std::endl;
uint160 f;
if(isHexFamily(family))
f.SetHex(family);
else if(isHexPublicKey(family))
f=findFamilyPK(family);
else
f=findFamilySN(family);
if(!f) return LocalAccount::pointer();
return getLocalAccount(f, boost::lexical_cast<int>(seq));
}
uint160 Wallet::peekKey(const uint160& family, int seq)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(family);
if(fit==mFamilies.end()) return uint160();
return fit->second->getAccount(seq, false);
}
void Wallet::delFamily(const uint160& familyName)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(familyName);
if(fit==mFamilies.end()) return;
std::map<int, LocalAccount::pointer>& acctMap=fit->second->getAcctMap();
for(std::map<int, LocalAccount::pointer>::iterator it=acctMap.begin(); it!=acctMap.end(); ++it)
mAccounts.erase(it->second->getAddress());
mFamilies.erase(familyName);
}
LocalAccountFamily::pointer Wallet::doPublic(const std::string& pubKey, bool do_create, bool do_db)
{
// Generate root key
EC_KEY *pkey=CKey::GenerateRootPubKey(pubKey);
if(!pkey)
{
#ifdef DEBUG
std::cerr << "Unable to generate root public key" << std::endl;
assert(false);
#endif
return LocalAccountFamily::pointer();
}
// Extract family name
std::vector<unsigned char> rootPubKey(33, 0);
unsigned char *begin=&rootPubKey[0];
i2o_ECPublicKey(pkey, &begin);
while(rootPubKey.size()<33) rootPubKey.push_back((unsigned char)0);
uint160 family=NewcoinAddress(rootPubKey).GetHash160();
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(family);
if(fit!=mFamilies.end()) // already added
{
EC_KEY_free(pkey);
return fit->second;
}
if(!do_create)
{
EC_KEY_free(pkey);
return LocalAccountFamily::pointer();
}
LocalAccountFamily::pointer fam;
if(do_db)
{
fam=LocalAccountFamily::readFamily(family);
if(fam) do_create=false;
}
if(do_create)
{
fam=boost::make_shared<LocalAccountFamily>(family, EC_KEY_get0_group(pkey), EC_KEY_get0_public_key(pkey));
mFamilies.insert(std::make_pair(family, fam));
if(do_db) fam->write(true);
}
sl.unlock();
EC_KEY_free(pkey);
return fam;
}
LocalAccountFamily::pointer Wallet::doPrivate(const uint256& passPhrase, bool do_create, bool do_unlock)
{
// Generate root key
EC_KEY *base=CKey::GenerateRootDeterministicKey(passPhrase);
// Extract family name
std::vector<unsigned char> rootPubKey(33, 0);
unsigned char *begin=&rootPubKey[0];
i2o_ECPublicKey(base, &begin);
while(rootPubKey.size()<33) rootPubKey.push_back((unsigned char)0);
uint160 family=NewcoinAddress(rootPubKey).GetHash160();
boost::recursive_mutex::scoped_lock sl(mLock);
LocalAccountFamily::pointer fam;
std::map<uint160, LocalAccountFamily::pointer>::iterator it=mFamilies.find(family);
if(it==mFamilies.end())
{ // family not found
fam=LocalAccountFamily::readFamily(family);
if(!fam)
{
if(!do_create)
{
EC_KEY_free(base);
return LocalAccountFamily::pointer();
}
fam=boost::make_shared<LocalAccountFamily>(family,
EC_KEY_get0_group(base), EC_KEY_get0_public_key(base));
mFamilies.insert(std::make_pair(family, fam));
fam->write(true);
}
}
else fam=it->second;
if(do_unlock && fam->isLocked())
fam->unlock(EC_KEY_get0_private_key(base));
sl.unlock();
EC_KEY_free(base);
return fam;
}
bool Wallet::lock(const uint160& family)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.find(family);
if(fit==mFamilies.end()) return false;
fit->second->lock();
return true;
}
void Wallet::lock()
{
boost::recursive_mutex::scoped_lock sl(mLock);
for(std::map<uint160, LocalAccountFamily::pointer>::iterator fit=mFamilies.begin();
fit!=mFamilies.end(); ++fit)
fit->second->lock();
}
bool Wallet::unitTest()
{ // Create 100 keys for each of 1,000 families and ensure all keys match
Wallet pub, priv;
uint256 privBase(time(NULL)^(getpid()<<16));
// privBase.SetHex("102031459e");
for(int i=0; i<1000; i++, privBase++)
{
uint160 fam=priv.addFamily(privBase, false);
#ifdef DEBUG
std::cerr << "Priv: " << privBase.GetHex() << " Fam: " << fam.GetHex() << std::endl;
#endif
std::string pubGen=priv.getPubGenHex(fam);
#ifdef DEBUG
std::cerr << "Pub: " << pubGen << std::endl;
#endif
if(pub.addFamily(pubGen)!=fam)
{
assert(false);
return false;
}
if(pub.getPubGenHex(fam)!=pubGen)
{
#ifdef DEBUG
std::cerr << std::endl;
std::cerr << "PuK: " << pub.getPubGenHex(fam) << std::endl;
std::cerr << "PrK: " << priv.getPubGenHex(fam) << std::endl;
std::cerr << "Fam: " << fam.GetHex() << std::endl;
#endif
assert(false);
return false;
}
for(int j=0; j<100; j++)
{
LocalAccount::pointer lpub=pub.getLocalAccount(fam, j);
LocalAccount::pointer lpriv=priv.getLocalAccount(fam, j);
if(!lpub || !lpriv)
{
assert(false);
return false;
}
uint160 lpuba=lpub->getAddress();
uint160 lpriva=lpriv->getAddress();
#ifdef DEBUG
// std::cerr << "pubA(" << j << "): " << lpuba.GetHex() << std::endl;
// std::cerr << "prvA(" << j << "): " << lpriva.GetHex() << std::endl;
std::cerr << "." << std::flush;
#endif
if(!lpuba || (lpuba!=lpriva))
{
assert(false);
return false;
}
}
std::cerr << std::endl;
pub.delFamily(fam);
priv.delFamily(fam);
}
return true;
}
void Wallet::syncToLedger(bool force, Ledger* ledger)
{
boost::recursive_mutex::scoped_lock sl(mLock);
if(!force && (mLedger>=ledger->getLedgerSeq())) return;
for(std::map<uint256, LocalTransaction::pointer>::iterator xit=mTransactions.begin();
xit!=mTransactions.end(); ++xit)
{ // check each transaction, see if it's in the ledger or allowed in the ledger
// WRITEME
}
for(std::map<uint160, LocalAccount::pointer>::iterator ait=mAccounts.begin(); ait!=mAccounts.end(); ++ait)
{ // check each account, see if our ledger balance matches
LocalAccount::pointer& lac=ait->second;
AccountState::pointer acs=ledger->getAccountState(ait->first);
if(!acs) lac->setLedgerBalance(0);
else lac->setLedgerBalance(acs->getBalance());
}
if(mLedger<ledger->getLedgerSeq()) mLedger=ledger->getLedgerSeq();
}
void Wallet::applyTransaction(Transaction::pointer txn)
{
TransStatus st=txn->getStatus();
bool shouldBePaid=(st==INCLUDED) || (st==HELD) || (st==NEW);
LocalTransaction::pointer ltx;
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint256, LocalTransaction::pointer>::iterator lti=mTransactions.find(txn->getID());
if(lti!=mTransactions.end()) ltx=lti->second;
std::map<uint160, LocalAccount::pointer>::iterator lac=mAccounts.find(txn->getToAccount());
if(lac!=mAccounts.end())
{ // this is to a local account
if(!ltx)
{ // this is to a local account, and we don't have a local transaction for it
ltx=boost::make_shared<LocalTransaction>
(txn->getToAccount(), txn->getAmount(), txn->getIdent());
ltx->setTransaction(txn);
mTransactions.insert(std::make_pair<uint256, LocalTransaction::pointer>(txn->getID(), ltx));
ltx->setCredited();
lac->second->credit(txn->getAmount()-txn->getFee());
}
}
lac=mAccounts.find(txn->getFromAccount());
if(lac==mAccounts.end()) return;
if ( (st!=INVALID) && (lac->second->getTxnSeq()==txn->getFromAccountSeq()) )
lac->second->incTxnSeq();
if(!ltx)
{ // we don't have this transactions
if(shouldBePaid)
{ // we need it
ltx=boost::make_shared<LocalTransaction>
(txn->getToAccount(), txn->getAmount(), txn->getIdent());
ltx->setTransaction(txn);
mTransactions.insert(std::make_pair<uint256, LocalTransaction::pointer>(txn->getID(), ltx));
lac->second->debit(txn->getAmount());
ltx->setPaid();
}
}
else
{ // we have this transaction in some form (ltx->second)
if( (st==COMMITTED) || (st==INVALID) )
{ // we need to remove it
if(ltx->isPaid())
{
lac->second->credit(txn->getAmount());
ltx->setUnpaid();
}
mTransactions.erase(txn->getID());
}
else if(ltx->isPaid() && !shouldBePaid)
{ // we paid for this transaction and it didn't happen
lac->second->credit(txn->getAmount());
ltx->setUnpaid();
}
else if(!ltx->isPaid() && shouldBePaid)
{ // this transaction happened, and we haven't paid locally
lac->second->debit(txn->getAmount());
ltx->setPaid();
}
}
}
void Wallet::addLocalTransactions(Json::Value& ret)
{
boost::recursive_mutex::scoped_lock sl(mLock);
for(std::map<uint256, LocalTransaction::pointer>::iterator it=mTransactions.begin();
it!=mTransactions.end(); ++it)
ret[it->first.GetHex()]=it->second->getJson();
}
bool Wallet::getTxJson(const uint256& txn, Json::Value& ret)
{
boost::recursive_mutex::scoped_lock sl(mLock);
std::map<uint256, LocalTransaction::pointer>::iterator it=mTransactions.find(txn);
if(it==mTransactions.end()) return false;
ret=it->second->getJson();
return true;
}
bool Wallet::getTxsJson(const uint160& account, Json::Value& ret)
{
boost::recursive_mutex::scoped_lock sl(mLock);
for(std::map<uint256, LocalTransaction::pointer>::iterator it=mTransactions.begin();
it!=mTransactions.end(); ++it)
{
Transaction::pointer txn=it->second->getTransaction();
if(txn && ((account==txn->getFromAccount())||(account==txn->getToAccount())) )
ret[it->first.GetHex()]=it->second->getJson();
}
return true;
}

92
src/Wallet.h Normal file
View File

@@ -0,0 +1,92 @@
#ifndef __WALLET__
#define __WALLET__
#include <vector>
#include <map>
#include <string>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/shared_ptr.hpp>
#include "openssl/ec.h"
#include "../json/value.h"
#include "uint256.h"
#include "Serializer.h"
#include "LocalAccount.h"
#include "LocalTransaction.h"
class Ledger;
class Wallet
{
protected:
boost::recursive_mutex mLock;
std::map<uint160, LocalAccountFamily::pointer> mFamilies;
std::map<uint160, LocalAccount::pointer> mAccounts;
std::map<uint256, LocalTransaction::pointer> mTransactions;
uint32 mLedger; // ledger we last synched to
LocalAccountFamily::pointer doPrivate(const uint256& passPhrase, bool do_create, bool do_unlock);
LocalAccountFamily::pointer doPublic(const std::string& pubKey, bool do_create, bool do_db);
void addFamily(const uint160& family, const std::string& pubKey, int seq,
const std::string& name, const std::string& comment);
public:
Wallet() : mLedger(0) { ; }
uint160 addFamily(const std::string& passPhrase, bool lock);
uint160 addFamily(const uint256& passPhrase, bool lock);
uint160 addFamily(const std::string& pubKey);
uint160 addRandomFamily(uint256& privKey);
uint160 findFamilySN(const std::string& shortName);
uint160 findFamilyPK(const std::string& pubKey);
void delFamily(const uint160& familyName);
void getFamilies(std::vector<uint160>& familyIDs);
uint160 unlock(const uint256& passPhrase);
bool lock(const uint160& familyName);
void lock();
void load();
// must be a known local account
LocalAccount::pointer parseAccount(const std::string& accountSpecifier);
LocalAccount::pointer getLocalAccount(const uint160& famBase, int seq);
LocalAccount::pointer getLocalAccount(const uint160& acctID);
LocalAccount::pointer getNewLocalAccount(const uint160& family);
LocalAccount::pointer findAccountForTransaction(uint64 amount);
uint160 peekKey(const uint160& family, int seq);
std::string getPubGenHex(const uint160& famBase);
std::string getShortName(const uint160& famBase);
bool getFamilyInfo(const uint160& family, std::string& name, std::string& comment);
bool getFullFamilyInfo(const uint160& family, std::string& name, std::string& comment,
std::string& pubGen, bool& isLocked);
Json::Value getFamilyJson(const uint160& family);
bool getTxJson(const uint256& txid, Json::Value& value);
bool getTxsJson(const uint160& acctid, Json::Value& value);
void addLocalTransactions(Json::Value&);
static bool isHexPrivateKey(const std::string&);
static bool isHexPublicKey(const std::string&);
static bool isHexFamily(const std::string&);
static std::string privKeyToText(const uint256& privKey);
static uint256 textToPrivKey(const std::string&);
void syncToLedger(bool force, Ledger* ledger);
void applyTransaction(Transaction::pointer);
static bool unitTest();
};
#endif

245
src/base58.h Normal file
View File

@@ -0,0 +1,245 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin Developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
//
// Why base-58 instead of standard base-64 encoding?
// - Don't want 0OIl characters that look the same in some fonts and
// could be used to create visually identical looking account numbers.
// - A string with non-alphanumeric characters is not as easily accepted as an account number.
// - E-mail usually won't line-break if there's no punctuation to break at.
// - Doubleclicking selects the whole number as one word if it's all alphanumeric.
//
#ifndef BITCOIN_BASE58_H
#define BITCOIN_BASE58_H
#include <string>
#include <algorithm>
#include <vector>
#include "bignum.h"
#include "BitcoinUtil.h"
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend)
{
CAutoBN_CTX pctx;
CBigNum bn58 = 58;
CBigNum bn0 = 0;
// Convert big endian data to little endian
// Extra zero at the end make sure bignum will interpret as a positive number
std::vector<unsigned char> vchTmp(pend-pbegin+1, 0);
std::reverse_copy(pbegin, pend, vchTmp.begin());
// Convert little endian data to bignum
CBigNum bn;
bn.setvch(vchTmp);
// Convert bignum to std::string
std::string str;
// Expected size increase from base58 conversion is approximately 137%
// use 138% to be safe
str.reserve((pend - pbegin) * 138 / 100 + 1);
CBigNum dv;
CBigNum rem;
while (bn > bn0)
{
if (!BN_div(&dv, &rem, &bn, &bn58, pctx))
throw bignum_error("EncodeBase58 : BN_div failed");
bn = dv;
unsigned int c = rem.getulong();
str += pszBase58[c];
}
// Leading zeroes encoded as base58 zeros
for (const unsigned char* p = pbegin; p < pend && *p == 0; p++)
str += pszBase58[0];
// Convert little endian std::string to big endian
reverse(str.begin(), str.end());
return str;
}
inline std::string EncodeBase58(const std::vector<unsigned char>& vch)
{
return EncodeBase58(&vch[0], &vch[0] + vch.size());
}
inline bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet)
{
CAutoBN_CTX pctx;
vchRet.clear();
CBigNum bn58 = 58;
CBigNum bn = 0;
CBigNum bnChar;
while (isspace(*psz))
psz++;
// Convert big endian string to bignum
for (const char* p = psz; *p; p++)
{
const char* p1 = strchr(pszBase58, *p);
if (p1 == NULL)
{
while (isspace(*p))
p++;
if (*p != '\0')
return false;
break;
}
bnChar.setulong(p1 - pszBase58);
if (!BN_mul(&bn, &bn, &bn58, pctx))
throw bignum_error("DecodeBase58 : BN_mul failed");
bn += bnChar;
}
// Get bignum as little endian data
std::vector<unsigned char> vchTmp = bn.getvch();
// Trim off sign byte if present
if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80)
vchTmp.erase(vchTmp.end()-1);
// Restore leading zeros
int nLeadingZeros = 0;
for (const char* p = psz; *p == pszBase58[0]; p++)
nLeadingZeros++;
vchRet.assign(nLeadingZeros + vchTmp.size(), 0);
// Convert little endian data to big endian
std::reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size());
return true;
}
inline bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet)
{
return DecodeBase58(str.c_str(), vchRet);
}
inline std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn)
{
// add 4-byte hash check to the end
std::vector<unsigned char> vch(vchIn);
uint256 hash = SHA256Hash(vch.begin(), vch.end());
vch.insert(vch.end(), (unsigned char*)&hash, (unsigned char*)&hash + 4);
return EncodeBase58(vch);
}
inline bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet)
{
if (!DecodeBase58(psz, vchRet))
return false;
if (vchRet.size() < 4)
{
vchRet.clear();
return false;
}
uint256 hash = SHA256Hash(vchRet.begin(), vchRet.end()-4);
if (memcmp(&hash, &vchRet.end()[-4], 4) != 0)
{
vchRet.clear();
return false;
}
vchRet.resize(vchRet.size()-4);
return true;
}
inline bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet)
{
return DecodeBase58Check(str.c_str(), vchRet);
}
class CBase58Data
{
protected:
unsigned char nVersion;
std::vector<unsigned char> vchData;
CBase58Data()
{
nVersion = 1;
vchData.clear();
}
~CBase58Data()
{
if (!vchData.empty())
memset(&vchData[0], 0, vchData.size());
}
void SetData(int nVersionIn, const void* pdata, size_t nSize)
{
nVersion = nVersionIn;
vchData.resize(nSize);
if (!vchData.empty())
memcpy(&vchData[0], pdata, nSize);
}
void SetData(int nVersionIn, const unsigned char *pbegin, const unsigned char *pend)
{
SetData(nVersionIn, (void*)pbegin, pend - pbegin);
}
public:
bool SetString(const char* psz)
{
std::vector<unsigned char> vchTemp;
DecodeBase58Check(psz, vchTemp);
if (vchTemp.empty())
{
vchData.clear();
nVersion = 0;
return false;
}
nVersion = vchTemp[0];
vchData.resize(vchTemp.size() - 1);
if (!vchData.empty())
memcpy(&vchData[0], &vchTemp[1], vchData.size());
memset(&vchTemp[0], 0, vchTemp.size());
return true;
}
bool SetString(const std::string& str)
{
return SetString(str.c_str());
}
std::string ToString() const
{
std::vector<unsigned char> vch(1, nVersion);
vch.insert(vch.end(), vchData.begin(), vchData.end());
return EncodeBase58Check(vch);
}
int CompareTo(const CBase58Data& b58) const
{
if (nVersion < b58.nVersion) return -1;
if (nVersion > b58.nVersion) return 1;
if (vchData < b58.vchData) return -1;
if (vchData > b58.vchData) return 1;
return 0;
}
bool operator==(const CBase58Data& b58) const { return CompareTo(b58) == 0; }
bool operator<=(const CBase58Data& b58) const { return CompareTo(b58) <= 0; }
bool operator>=(const CBase58Data& b58) const { return CompareTo(b58) >= 0; }
bool operator< (const CBase58Data& b58) const { return CompareTo(b58) < 0; }
bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; }
};
#endif

538
src/bignum.h Normal file
View File

@@ -0,0 +1,538 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_BIGNUM_H
#define BITCOIN_BIGNUM_H
#include <stdexcept>
#include <vector>
#include <openssl/bn.h>
#include "BitcoinUtil.h"
class bignum_error : public std::runtime_error
{
public:
explicit bignum_error(const std::string& str) : std::runtime_error(str) {}
};
class CAutoBN_CTX
{
protected:
BN_CTX* pctx;
BN_CTX* operator=(BN_CTX* pnew) { return pctx = pnew; }
public:
CAutoBN_CTX()
{
pctx = BN_CTX_new();
if (pctx == NULL)
throw bignum_error("CAutoBN_CTX : BN_CTX_new() returned NULL");
}
~CAutoBN_CTX()
{
if (pctx != NULL)
BN_CTX_free(pctx);
}
operator BN_CTX*() { return pctx; }
BN_CTX& operator*() { return *pctx; }
BN_CTX** operator&() { return &pctx; }
bool operator!() { return (pctx == NULL); }
};
class CBigNum : public BIGNUM
{
public:
CBigNum()
{
BN_init(this);
}
CBigNum(const CBigNum& b)
{
BN_init(this);
if (!BN_copy(this, &b))
{
BN_clear_free(this);
throw bignum_error("CBigNum::CBigNum(const CBigNum&) : BN_copy failed");
}
}
CBigNum& operator=(const CBigNum& b)
{
if (!BN_copy(this, &b))
throw bignum_error("CBigNum::operator= : BN_copy failed");
return (*this);
}
~CBigNum()
{
BN_clear_free(this);
}
CBigNum(char n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); }
CBigNum(short n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); }
CBigNum(int n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); }
CBigNum(long n) { BN_init(this); if (n >= 0) setulong(n); else setint64(n); }
CBigNum(int64 n) { BN_init(this); setint64(n); }
CBigNum(unsigned char n) { BN_init(this); setulong(n); }
CBigNum(unsigned short n) { BN_init(this); setulong(n); }
CBigNum(unsigned int n) { BN_init(this); setulong(n); }
CBigNum(unsigned long n) { BN_init(this); setulong(n); }
CBigNum(uint64 n) { BN_init(this); setuint64(n); }
explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); }
explicit CBigNum(const std::vector<unsigned char>& vch)
{
BN_init(this);
setvch(vch);
}
void setulong(unsigned long n)
{
if (!BN_set_word(this, n))
throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed");
}
unsigned long getulong() const
{
return BN_get_word(this);
}
unsigned int getuint() const
{
return BN_get_word(this);
}
int getint() const
{
unsigned long n = BN_get_word(this);
if (!BN_is_negative(this))
return (n > INT_MAX ? INT_MAX : n);
else
return (n > INT_MAX ? INT_MIN : -(int)n);
}
void setint64(int64 n)
{
unsigned char pch[sizeof(n) + 6];
unsigned char* p = pch + 4;
bool fNegative = false;
if (n < (int64)0)
{
n = -n;
fNegative = true;
}
bool fLeadingZeroes = true;
for (int i = 0; i < 8; i++)
{
unsigned char c = (n >> 56) & 0xff;
n <<= 8;
if (fLeadingZeroes)
{
if (c == 0)
continue;
if (c & 0x80)
*p++ = (fNegative ? 0x80 : 0);
else if (fNegative)
c |= 0x80;
fLeadingZeroes = false;
}
*p++ = c;
}
unsigned int nSize = p - (pch + 4);
pch[0] = (nSize >> 24) & 0xff;
pch[1] = (nSize >> 16) & 0xff;
pch[2] = (nSize >> 8) & 0xff;
pch[3] = (nSize) & 0xff;
BN_mpi2bn(pch, p - pch, this);
}
void setuint64(uint64 n)
{
unsigned char pch[sizeof(n) + 6];
unsigned char* p = pch + 4;
bool fLeadingZeroes = true;
for (int i = 0; i < 8; i++)
{
unsigned char c = (n >> 56) & 0xff;
n <<= 8;
if (fLeadingZeroes)
{
if (c == 0)
continue;
if (c & 0x80)
*p++ = 0;
fLeadingZeroes = false;
}
*p++ = c;
}
unsigned int nSize = p - (pch + 4);
pch[0] = (nSize >> 24) & 0xff;
pch[1] = (nSize >> 16) & 0xff;
pch[2] = (nSize >> 8) & 0xff;
pch[3] = (nSize) & 0xff;
BN_mpi2bn(pch, p - pch, this);
}
void setuint256(uint256 n)
{
unsigned char pch[sizeof(n) + 6];
unsigned char* p = pch + 4;
bool fLeadingZeroes = true;
unsigned char* pbegin = (unsigned char*)&n;
unsigned char* psrc = pbegin + sizeof(n);
while (psrc != pbegin)
{
unsigned char c = *(--psrc);
if (fLeadingZeroes)
{
if (c == 0)
continue;
if (c & 0x80)
*p++ = 0;
fLeadingZeroes = false;
}
*p++ = c;
}
unsigned int nSize = p - (pch + 4);
pch[0] = (nSize >> 24) & 0xff;
pch[1] = (nSize >> 16) & 0xff;
pch[2] = (nSize >> 8) & 0xff;
pch[3] = (nSize >> 0) & 0xff;
BN_mpi2bn(pch, p - pch, this);
}
uint256 getuint256()
{
unsigned int nSize = BN_bn2mpi(this, NULL);
if (nSize < 4)
return 0;
std::vector<unsigned char> vch(nSize);
BN_bn2mpi(this, &vch[0]);
if (vch.size() > 4)
vch[4] &= 0x7f;
uint256 n = 0;
for (int i = 0, j = vch.size()-1; i < sizeof(n) && j >= 4; i++, j--)
((unsigned char*)&n)[i] = vch[j];
return n;
}
void setvch(const std::vector<unsigned char>& vch)
{
std::vector<unsigned char> vch2(vch.size() + 4);
unsigned int nSize = vch.size();
// BIGNUM's byte stream format expects 4 bytes of
// big endian size data info at the front
vch2[0] = (nSize >> 24) & 0xff;
vch2[1] = (nSize >> 16) & 0xff;
vch2[2] = (nSize >> 8) & 0xff;
vch2[3] = (nSize >> 0) & 0xff;
// swap data to big endian
std::reverse_copy(vch.begin(), vch.end(), vch2.begin() + 4);
BN_mpi2bn(&vch2[0], vch2.size(), this);
}
std::vector<unsigned char> getvch() const
{
unsigned int nSize = BN_bn2mpi(this, NULL);
if (nSize < 4)
return std::vector<unsigned char>();
std::vector<unsigned char> vch(nSize);
BN_bn2mpi(this, &vch[0]);
vch.erase(vch.begin(), vch.begin() + 4);
reverse(vch.begin(), vch.end());
return vch;
}
CBigNum& SetCompact(unsigned int nCompact)
{
unsigned int nSize = nCompact >> 24;
std::vector<unsigned char> vch(4 + nSize);
vch[3] = nSize;
if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff;
if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff;
if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff;
BN_mpi2bn(&vch[0], vch.size(), this);
return *this;
}
unsigned int GetCompact() const
{
unsigned int nSize = BN_bn2mpi(this, NULL);
std::vector<unsigned char> vch(nSize);
nSize -= 4;
BN_bn2mpi(this, &vch[0]);
unsigned int nCompact = nSize << 24;
if (nSize >= 1) nCompact |= (vch[4] << 16);
if (nSize >= 2) nCompact |= (vch[5] << 8);
if (nSize >= 3) nCompact |= (vch[6] << 0);
return nCompact;
}
void SetHex(const std::string& str)
{
// skip 0x
const char* psz = str.c_str();
while (isspace(*psz))
psz++;
bool fNegative = false;
if (*psz == '-')
{
fNegative = true;
psz++;
}
if (psz[0] == '0' && tolower(psz[1]) == 'x')
psz += 2;
while (isspace(*psz))
psz++;
// hex string to bignum
static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 };
*this = 0;
while (isxdigit(*psz))
{
*this <<= 4;
int n = phexdigit[(int) *psz++];
*this += n;
}
if (fNegative)
*this = 0 - *this;
}
std::string ToString(int nBase=10) const
{
CAutoBN_CTX pctx;
CBigNum bnBase = nBase;
CBigNum bn0 = 0;
std::string str;
CBigNum bn = *this;
BN_set_negative(&bn, false);
CBigNum dv;
CBigNum rem;
if (BN_cmp(&bn, &bn0) == 0)
return "0";
while (BN_cmp(&bn, &bn0) > 0)
{
if (!BN_div(&dv, &rem, &bn, &bnBase, pctx))
throw bignum_error("CBigNum::ToString() : BN_div failed");
bn = dv;
unsigned int c = rem.getulong();
str += "0123456789abcdef"[c];
}
if (BN_is_negative(this))
str += "-";
reverse(str.begin(), str.end());
return str;
}
std::string GetHex() const
{
return ToString(16);
}
/* JED
unsigned int GetSerializeSize(int nType=0, int nVersion=VERSION) const
{
return ::GetSerializeSize(getvch(), nType, nVersion);
}
template<typename Stream>
void Serialize(Stream& s, int nType=0, int nVersion=VERSION) const
{
::Serialize(s, getvch(), nType, nVersion);
}
template<typename Stream>
void Unserialize(Stream& s, int nType=0, int nVersion=VERSION)
{
std::vector<unsigned char> vch;
::Unserialize(s, vch, nType, nVersion);
setvch(vch);
}*/
bool operator!() const
{
return BN_is_zero(this);
}
CBigNum& operator+=(const CBigNum& b)
{
if (!BN_add(this, this, &b))
throw bignum_error("CBigNum::operator+= : BN_add failed");
return *this;
}
CBigNum& operator-=(const CBigNum& b)
{
*this = *this - b;
return *this;
}
CBigNum& operator*=(const CBigNum& b)
{
CAutoBN_CTX pctx;
if (!BN_mul(this, this, &b, pctx))
throw bignum_error("CBigNum::operator*= : BN_mul failed");
return *this;
}
CBigNum& operator/=(const CBigNum& b)
{
*this = *this / b;
return *this;
}
CBigNum& operator%=(const CBigNum& b)
{
*this = *this % b;
return *this;
}
CBigNum& operator<<=(unsigned int shift)
{
if (!BN_lshift(this, this, shift))
throw bignum_error("CBigNum:operator<<= : BN_lshift failed");
return *this;
}
CBigNum& operator>>=(unsigned int shift)
{
// Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number
// if built on ubuntu 9.04 or 9.10, probably depends on version of openssl
CBigNum a = 1;
a <<= shift;
if (BN_cmp(&a, this) > 0)
{
*this = 0;
return *this;
}
if (!BN_rshift(this, this, shift))
throw bignum_error("CBigNum:operator>>= : BN_rshift failed");
return *this;
}
CBigNum& operator++()
{
// prefix operator
if (!BN_add(this, this, BN_value_one()))
throw bignum_error("CBigNum::operator++ : BN_add failed");
return *this;
}
const CBigNum operator++(int)
{
// postfix operator
const CBigNum ret = *this;
++(*this);
return ret;
}
CBigNum& operator--()
{
// prefix operator
CBigNum r;
if (!BN_sub(&r, this, BN_value_one()))
throw bignum_error("CBigNum::operator-- : BN_sub failed");
*this = r;
return *this;
}
const CBigNum operator--(int)
{
// postfix operator
const CBigNum ret = *this;
--(*this);
return ret;
}
friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b);
friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b);
friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b);
};
inline const CBigNum operator+(const CBigNum& a, const CBigNum& b)
{
CBigNum r;
if (!BN_add(&r, &a, &b))
throw bignum_error("CBigNum::operator+ : BN_add failed");
return r;
}
inline const CBigNum operator-(const CBigNum& a, const CBigNum& b)
{
CBigNum r;
if (!BN_sub(&r, &a, &b))
throw bignum_error("CBigNum::operator- : BN_sub failed");
return r;
}
inline const CBigNum operator-(const CBigNum& a)
{
CBigNum r(a);
BN_set_negative(&r, !BN_is_negative(&r));
return r;
}
inline const CBigNum operator*(const CBigNum& a, const CBigNum& b)
{
CAutoBN_CTX pctx;
CBigNum r;
if (!BN_mul(&r, &a, &b, pctx))
throw bignum_error("CBigNum::operator* : BN_mul failed");
return r;
}
inline const CBigNum operator/(const CBigNum& a, const CBigNum& b)
{
CAutoBN_CTX pctx;
CBigNum r;
if (!BN_div(&r, NULL, &a, &b, pctx))
throw bignum_error("CBigNum::operator/ : BN_div failed");
return r;
}
inline const CBigNum operator%(const CBigNum& a, const CBigNum& b)
{
CAutoBN_CTX pctx;
CBigNum r;
if (!BN_mod(&r, &a, &b, pctx))
throw bignum_error("CBigNum::operator% : BN_div failed");
return r;
}
inline const CBigNum operator<<(const CBigNum& a, unsigned int shift)
{
CBigNum r;
if (!BN_lshift(&r, &a, shift))
throw bignum_error("CBigNum:operator<< : BN_lshift failed");
return r;
}
inline const CBigNum operator>>(const CBigNum& a, unsigned int shift)
{
CBigNum r = a;
r >>= shift;
return r;
}
inline bool operator==(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) == 0); }
inline bool operator!=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) != 0); }
inline bool operator<=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) <= 0); }
inline bool operator>=(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) >= 0); }
inline bool operator<(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) < 0); }
inline bool operator>(const CBigNum& a, const CBigNum& b) { return (BN_cmp(&a, &b) > 0); }
#endif

278
src/key.h Normal file
View File

@@ -0,0 +1,278 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_KEY_H
#define BITCOIN_KEY_H
#include <stdexcept>
#include <vector>
#include <cassert>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#include <boost/shared_ptr.hpp>
#include "SecureAllocator.h"
#include "NewcoinAddress.h"
#include "uint256.h"
#include "base58.h"
// secp256k1:
// const unsigned int PRIVATE_KEY_SIZE = 279;
// const unsigned int PUBLIC_KEY_SIZE = 65; // but we don't use full keys
// const unsigned int COMPUB_KEY_SIZE = 33;
// const unsigned int SIGNATURE_SIZE = 72;
//
// see www.keylength.com
// script supports up to 75 for single byte push
int static inline EC_KEY_regenerate_key(EC_KEY *eckey, BIGNUM *priv_key)
{
int ok = 0;
BN_CTX *ctx = NULL;
EC_POINT *pub_key = NULL;
if (!eckey) return 0;
const EC_GROUP *group = EC_KEY_get0_group(eckey);
if ((ctx = BN_CTX_new()) == NULL)
goto err;
pub_key = EC_POINT_new(group);
if (pub_key == NULL)
goto err;
if (!EC_POINT_mul(group, pub_key, priv_key, NULL, NULL, ctx))
goto err;
EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
EC_KEY_set_private_key(eckey, priv_key);
EC_KEY_set_public_key(eckey, pub_key);
ok = 1;
err:
if (pub_key)
EC_POINT_free(pub_key);
if (ctx != NULL)
BN_CTX_free(ctx);
return(ok);
}
class key_error : public std::runtime_error
{
public:
explicit key_error(const std::string& str) : std::runtime_error(str) {}
};
//JED: typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
//typedef std::vector<unsigned char, secure_allocator<unsigned char> > CSecret;
typedef std::vector<unsigned char > CPrivKey;
typedef std::vector<unsigned char > CSecret;
class CKey
{
protected:
EC_KEY* pkey;
bool fSet;
public:
typedef boost::shared_ptr<CKey> pointer;
CKey()
{
pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
if (pkey == NULL)
throw key_error("CKey::CKey() : EC_KEY_new_by_curve_name failed");
fSet = false;
}
CKey(const CKey& b)
{
pkey = EC_KEY_dup(b.pkey);
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
if (pkey == NULL)
throw key_error("CKey::CKey(const CKey&) : EC_KEY_dup failed");
fSet = b.fSet;
}
CKey& operator=(const CKey& b)
{
if (!EC_KEY_copy(pkey, b.pkey))
throw key_error("CKey::operator=(const CKey&) : EC_KEY_copy failed");
fSet = b.fSet;
return (*this);
}
~CKey()
{
EC_KEY_free(pkey);
}
static uint256 PassPhraseToKey(const std::string& passPhrase);
static EC_KEY* GenerateRootDeterministicKey(const uint256& passPhrase);
static EC_KEY* GenerateRootPubKey(const std::string& pubHex);
static EC_KEY* GeneratePublicDeterministicKey(const uint160& family, const EC_POINT* rootPub, int n);
static EC_KEY* GeneratePrivateDeterministicKey(const uint160& family, const BIGNUM* rootPriv, int n);
CKey(const uint256& passPhrase) : fSet(true)
{
pkey = GenerateRootDeterministicKey(passPhrase);
assert(pkey);
}
CKey(const uint160& base, const EC_POINT* rootPubKey, int n) : fSet(true)
{ // public deterministic key
pkey = GeneratePublicDeterministicKey(base, rootPubKey, n);
assert(pkey);
}
CKey(const uint160& base, const BIGNUM* rootPrivKey, int n) : fSet(true)
{ // private deterministic key
pkey = GeneratePrivateDeterministicKey(base, rootPrivKey, n);
assert(pkey);
}
bool IsNull() const
{
return !fSet;
}
void MakeNewKey()
{
if (!EC_KEY_generate_key(pkey))
throw key_error("CKey::MakeNewKey() : EC_KEY_generate_key failed");
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
fSet = true;
}
bool SetPrivKey(const CPrivKey& vchPrivKey)
{
const unsigned char* pbegin = &vchPrivKey[0];
if (!d2i_ECPrivateKey(&pkey, &pbegin, vchPrivKey.size()))
return false;
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
fSet = true;
return true;
}
bool SetSecret(const CSecret& vchSecret)
{
EC_KEY_free(pkey);
pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
if (pkey == NULL)
throw key_error("CKey::SetSecret() : EC_KEY_new_by_curve_name failed");
if (vchSecret.size() != 32)
throw key_error("CKey::SetSecret() : secret must be 32 bytes");
BIGNUM *bn = BN_bin2bn(&vchSecret[0], 32, BN_new());
if (bn == NULL)
throw key_error("CKey::SetSecret() : BN_bin2bn failed");
if (!EC_KEY_regenerate_key(pkey, bn))
throw key_error("CKey::SetSecret() : EC_KEY_regenerate_key failed");
BN_clear_free(bn);
fSet = true;
return true;
}
CSecret GetSecret() const
{
CSecret vchRet;
vchRet.resize(32);
const BIGNUM *bn = EC_KEY_get0_private_key(pkey);
int nBytes = BN_num_bytes(bn);
if (bn == NULL)
throw key_error("CKey::GetSecret() : EC_KEY_get0_private_key failed");
int n=BN_bn2bin(bn, &vchRet[32 - nBytes]);
if (n != nBytes)
throw key_error("CKey::GetSecret(): BN_bn2bin failed");
return vchRet;
}
CPrivKey GetPrivKey() const
{
unsigned int nSize = i2d_ECPrivateKey(pkey, NULL);
if (!nSize)
throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey failed");
assert(nSize<=279);
CPrivKey vchPrivKey(279, 0);
unsigned char* pbegin = &vchPrivKey[0];
if (i2d_ECPrivateKey(pkey, &pbegin) != nSize)
throw key_error("CKey::GetPrivKey() : i2d_ECPrivateKey returned unexpected size");
assert(vchPrivKey.size()<=279);
while(vchPrivKey.size()<279) vchPrivKey.push_back((unsigned char)0);
return vchPrivKey;
}
bool SetPubKey(const std::vector<unsigned char>& vchPubKey)
{
const unsigned char* pbegin = &vchPubKey[0];
if (!o2i_ECPublicKey(&pkey, &pbegin, vchPubKey.size()))
return false;
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
fSet = true;
return true;
}
std::vector<unsigned char> GetPubKey() const
{
unsigned int nSize = i2o_ECPublicKey(pkey, NULL);
assert(nSize<=33);
if (!nSize)
throw key_error("CKey::GetPubKey() : i2o_ECPublicKey failed");
std::vector<unsigned char> vchPubKey(33, 0);
unsigned char* pbegin = &vchPubKey[0];
if (i2o_ECPublicKey(pkey, &pbegin) != nSize)
throw key_error("CKey::GetPubKey() : i2o_ECPublicKey returned unexpected size");
assert(vchPubKey.size()<=33);
while(vchPubKey.size()<33) vchPubKey.push_back((unsigned char)0);
return vchPubKey;
}
bool Sign(const uint256& hash, std::vector<unsigned char>& vchSig)
{
vchSig.clear();
unsigned char pchSig[10000];
unsigned int nSize = 0;
if (!ECDSA_sign(0, (unsigned char*)&hash, sizeof(hash), pchSig, &nSize, pkey))
return false;
while(nSize<72)
{ // enlarge to 72 bytes
pchSig[nSize]=0;
nSize++;
}
assert(nSize==72);
vchSig.resize(nSize);
memcpy(&vchSig[0], pchSig, nSize);
return true;
}
bool Verify(const uint256& hash, const std::vector<unsigned char>& vchSig) const
{
// -1 = error, 0 = bad sig, 1 = good
if (ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), &vchSig[0], vchSig.size(), pkey) != 1)
return false;
return true;
}
NewcoinAddress GetAddress() const
{
return NewcoinAddress(GetPubKey());
}
};
#endif

36
src/keystore.cpp Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#include "keystore.h"
#include "BitcoinUtil.h"
#include <vector>
//#include "crypter.h"
std::vector<unsigned char> CKeyStore::GenerateNewKey()
{
RandAddSeedPerfmon();
CKey key;
key.MakeNewKey();
if (!AddKey(key))
throw std::runtime_error("CKeyStore::GenerateNewKey() : AddKey failed");
return key.GetPubKey();
}
bool CKeyStore::GetPubKey(const NewcoinAddress& address, std::vector<unsigned char>& vchPubKeyOut) const
{
CKey key;
if (!GetKey(address, key))
return false;
vchPubKeyOut = key.GetPubKey();
return true;
}
bool CBasicKeyStore::AddKey(const CKey& key)
{
mapKeys[key.GetAddress()] = key.GetSecret();
return true;
}

58
src/keystore.h Normal file
View File

@@ -0,0 +1,58 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_KEYSTORE_H
#define BITCOIN_KEYSTORE_H
#include "key.h"
//#include "crypter.h"
#include <map>
class CKeyStore
{
protected:
public:
virtual bool AddKey(const CKey& key) =0;
virtual bool HaveKey(const NewcoinAddress &address) const =0;
virtual bool GetKey(const NewcoinAddress &address, CKey& keyOut) const =0;
virtual bool GetPubKey(const NewcoinAddress &address, std::vector<unsigned char>& vchPubKeyOut) const;
virtual std::vector<unsigned char> GenerateNewKey();
};
typedef std::map<NewcoinAddress, CSecret> KeyMap;
class CBasicKeyStore : public CKeyStore
{
protected:
KeyMap mapKeys;
public:
bool AddKey(const CKey& key);
bool HaveKey(const NewcoinAddress &address) const
{
bool result;
result = (mapKeys.count(address) > 0);
return result;
}
bool GetKey(const NewcoinAddress &address, CKey& keyOut) const
{
{
KeyMap::const_iterator mi = mapKeys.find(address);
if (mi != mapKeys.end())
{
keyOut.SetSecret((*mi).second);
return true;
}
}
return false;
}
};
#endif

63
src/main.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "Application.h"
#include <iostream>
#include "CallRPC.h"
#include "Config.h"
extern void runTests();
using namespace std;
using namespace boost;
/*
Detect if another is running
If so message it with the users command
*/
void startApp()
{
theApp=new Application();
theApp->run(); // blocks till we get a stop RPC
}
void printHelp()
{
cout << "newcoin [options] <command> <params>" << endl;
cout << "options: " << endl;
cout << " -" << endl;
cout << "commands: " << endl;
cout << " createfamily [<key>]" << endl;
cout << " accountinfo <family>:<key>" << endl;
cout << " newaccount <family> [<name>]" << endl;
cout << " lock <family>" << endl;
cout << " unlock <passphrase>" << endl;
cout << " familyinfo" << endl;
cout << " connect <ip> [<port>]" << endl;
cout << " sendto <destination> <amount> [<tag>]" << endl;
cout << " tx" << endl;
cout << " ledger" << endl;
cout << " stop" << endl;
}
int parseCommandline(int argc, char* argv[])
{
int ret=0;
if(argc>1)
{
theConfig.load();
ret=commandLineRPC(argc, argv);
if(ret)
printHelp();
}
else startApp();
return ret;
}
int main(int argc, char* argv[])
{
// runTests();
return(parseCommandline(argc,argv));
}

249
src/rpc.cpp Normal file
View File

@@ -0,0 +1,249 @@
#include <boost/asio.hpp>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include "../json/value.h"
#include "../json/writer.h"
#include "RPC.h"
#include "BitcoinUtil.h"
#include "Config.h"
using namespace boost;
using namespace boost::asio;
Json::Value JSONRPCError(int code, const std::string& message)
{
Json::Value error(Json::objectValue);
error["code"]=Json::Value(code);
error["message"]=Json::Value(message);
return error;
}
//
// HTTP protocol
//
// This ain't Apache. We're just using HTTP header for the length field
// and to be compatible with other JSON-RPC implementations.
//
std::string createHTTPPost(const std::string& strMsg, const std::map<std::string, std::string>& mapRequestHeaders)
{
std::ostringstream s;
s << "POST / HTTP/1.1\r\n"
<< "User-Agent: coin-json-rpc/" << FormatFullVersion() << "\r\n"
<< "Host: 127.0.0.1\r\n"
<< "Content-Type: application/json\r\n"
<< "Content-Length: " << strMsg.size() << "\r\n"
<< "Accept: application/json\r\n";
typedef const std::pair<std::string, std::string> HeaderType;
BOOST_FOREACH(HeaderType& item, mapRequestHeaders)
s << item.first << ": " << item.second << "\r\n";
s << "\r\n" << strMsg;
return s.str();
}
std::string rfc1123Time()
{
char buffer[64];
time_t now;
time(&now);
struct tm* now_gmt = gmtime(&now);
std::string locale(setlocale(LC_TIME, NULL));
setlocale(LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
setlocale(LC_TIME, locale.c_str());
return std::string(buffer);
}
std::string HTTPReply(int nStatus, const std::string& strMsg)
{
std::cout << "HTTP Reply " << nStatus << " " << strMsg << std::endl;
if (nStatus == 401)
return strprintf("HTTP/1.0 401 Authorization Required\r\n"
"Date: %s\r\n"
"Server: coin-json-rpc/%s\r\n"
"WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 296\r\n"
"\r\n"
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
"\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
"<HTML>\r\n"
"<HEAD>\r\n"
"<TITLE>Error</TITLE>\r\n"
"<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
"</HEAD>\r\n"
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
"</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
std::string strStatus;
if (nStatus == 200) strStatus = "OK";
else if (nStatus == 400) strStatus = "Bad Request";
else if (nStatus == 403) strStatus = "Forbidden";
else if (nStatus == 404) strStatus = "Not Found";
else if (nStatus == 500) strStatus = "Internal Server Error";
return strprintf(
"HTTP/1.1 %d %s\r\n"
"Date: %s\r\n"
"Connection: close\r\n"
"Content-Length: %d\r\n"
"Content-Type: application/json\r\n"
"Server: coin-json-rpc/%s\r\n"
"\r\n"
"%s",
nStatus,
strStatus.c_str(),
rfc1123Time().c_str(),
strMsg.size(),
theConfig.VERSION_STR.c_str(),
strMsg.c_str());
}
int ReadHTTPStatus(std::basic_istream<char>& stream)
{
std::string str;
getline(stream, str);
std::vector<std::string> vWords;
boost::split(vWords, str, boost::is_any_of(" "));
if (vWords.size() < 2)
return 500;
return atoi(vWords[1].c_str());
}
int ReadHTTPHeader(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet)
{
int nLen = 0;
loop
{
std::string str;
std::getline(stream, str);
if (str.empty() || str == "\r")
break;
std::string::size_type nColon = str.find(":");
if (nColon != std::string::npos)
{
std::string strHeader = str.substr(0, nColon);
boost::trim(strHeader);
boost::to_lower(strHeader);
std::string strValue = str.substr(nColon+1);
boost::trim(strValue);
mapHeadersRet[strHeader] = strValue;
if (strHeader == "content-length")
nLen = atoi(strValue.c_str());
}
}
return nLen;
}
int ReadHTTP(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet,
std::string& strMessageRet)
{
mapHeadersRet.clear();
strMessageRet = "";
// Read status
int nStatus = ReadHTTPStatus(stream);
// Read header
int nLen = ReadHTTPHeader(stream, mapHeadersRet);
if (nLen < 0 || nLen > MAX_SIZE)
return 500;
// Read message
if (nLen > 0)
{
std::vector<char> vch(nLen);
stream.read(&vch[0], nLen);
strMessageRet = std::string(vch.begin(), vch.end());
}
return nStatus;
}
std::string DecodeBase64(std::string s)
{ // FIXME: This performs badly
BIO *b64, *bmem;
char* buffer = static_cast<char*>(calloc(s.size(), sizeof(char)));
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf(const_cast<char*>(s.c_str()), s.size());
bmem = BIO_push(b64, bmem);
BIO_read(bmem, buffer, s.size());
BIO_free_all(bmem);
std::string result(buffer);
free(buffer);
return result;
}
/*
bool HTTPAuthorized(map<std::string, std::string>& mapHeaders)
{
std::string strAuth = mapHeaders["authorization"];
if (strAuth.substr(0,6) != "Basic ")
return false;
std::string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
std::string strUserPass = DecodeBase64(strUserPass64);
std::string::size_type nColon = strUserPass.find(":");
if (nColon == std::string::npos)
return false;
std::string strUser = strUserPass.substr(0, nColon);
std::string strPassword = strUserPass.substr(nColon+1);
return (strUser == mapArgs["-rpcuser"] && strPassword == mapArgs["-rpcpassword"]);
}*/
//
// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
// unspecified (HTTP errors and contents of 'error').
//
// 1.0 spec: http://json-rpc.org/wiki/specification
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
//
std::string JSONRPCRequest(const std::string& strMethod, const Json::Value& params, const Json::Value& id)
{
Json::Value request;
request["method"]=strMethod;
request["params"]=params;
request["id"]=id;
Json::FastWriter writer;
return writer.write(request) + "\n";
}
std::string JSONRPCReply(const Json::Value& result, const Json::Value& error, const Json::Value& id)
{
Json::Value reply(Json::objectValue);
reply["result"]=result;
reply["error"]=error;
reply["id"]=id;
Json::FastWriter writer;
return writer.write(reply) + "\n";
}
void ErrorReply(std::ostream& stream, const Json::Value& objError, const Json::Value& id)
{
// Send error reply from json-rpc error object
int nStatus = 500;
int code = objError["code"].asInt();
if (code == -32600) nStatus = 400;
else if (code == -32601) nStatus = 404;
std::string strReply = JSONRPCReply(Json::Value(), objError, id);
stream << HTTPReply(nStatus, strReply) << std::flush;
}

403
src/script.h Normal file
View File

@@ -0,0 +1,403 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef H_BITCOIN_SCRIPT
#define H_BITCOIN_SCRIPT
#include "base58.h"
#include "keystore.h"
#include <string>
#include <vector>
#include <boost/foreach.hpp>
class CTransaction;
inline std::string ValueString(const std::vector<unsigned char>& vch)
{
if (vch.size() <= 4)
return strprintf("%d", CBigNum(vch).getint());
else
return HexStr(vch);
}
inline std::string StackString(const std::vector<std::vector<unsigned char> >& vStack)
{
std::string str;
BOOST_FOREACH(const std::vector<unsigned char>& vch, vStack)
{
if (!str.empty())
str += " ";
str += ValueString(vch);
}
return str;
}
class CScript : public std::vector<unsigned char>
{
protected:
CScript& push_int64(int64 n)
{
if (n == -1 || (n >= 1 && n <= 16))
{
push_back(n + (OP_1 - 1));
}
else
{
CBigNum bn(n);
*this << bn.getvch();
}
return *this;
}
CScript& push_uint64(uint64 n)
{
if (n >= 1 && n <= 16)
{
push_back(n + (OP_1 - 1));
}
else
{
CBigNum bn(n);
*this << bn.getvch();
}
return *this;
}
public:
CScript() { }
CScript(const CScript& b) : std::vector<unsigned char>(b.begin(), b.end()) { }
CScript(const_iterator pbegin, const_iterator pend) : std::vector<unsigned char>(pbegin, pend) { }
#ifndef _MSC_VER
CScript(const unsigned char* pbegin, const unsigned char* pend) : std::vector<unsigned char>(pbegin, pend) { }
#endif
CScript& operator+=(const CScript& b)
{
insert(end(), b.begin(), b.end());
return *this;
}
friend CScript operator+(const CScript& a, const CScript& b)
{
CScript ret = a;
ret += b;
return ret;
}
explicit CScript(char b) { operator<<(b); }
explicit CScript(short b) { operator<<(b); }
explicit CScript(int b) { operator<<(b); }
explicit CScript(long b) { operator<<(b); }
explicit CScript(int64 b) { operator<<(b); }
explicit CScript(unsigned char b) { operator<<(b); }
explicit CScript(unsigned int b) { operator<<(b); }
explicit CScript(unsigned short b) { operator<<(b); }
explicit CScript(unsigned long b) { operator<<(b); }
explicit CScript(uint64 b) { operator<<(b); }
explicit CScript(opcodetype b) { operator<<(b); }
explicit CScript(const uint256& b) { operator<<(b); }
explicit CScript(const CBigNum& b) { operator<<(b); }
explicit CScript(const std::vector<unsigned char>& b) { operator<<(b); }
CScript& operator<<(char b) { return push_int64(b); }
CScript& operator<<(short b) { return push_int64(b); }
CScript& operator<<(int b) { return push_int64(b); }
CScript& operator<<(long b) { return push_int64(b); }
CScript& operator<<(int64 b) { return push_int64(b); }
CScript& operator<<(unsigned char b) { return push_uint64(b); }
CScript& operator<<(unsigned int b) { return push_uint64(b); }
CScript& operator<<(unsigned short b) { return push_uint64(b); }
CScript& operator<<(unsigned long b) { return push_uint64(b); }
CScript& operator<<(uint64 b) { return push_uint64(b); }
CScript& operator<<(opcodetype opcode)
{
if (opcode < 0 || opcode > 0xff)
throw std::runtime_error("CScript::operator<<() : invalid opcode");
insert(end(), (unsigned char)opcode);
return *this;
}
CScript& operator<<(const uint160& b)
{
insert(end(), sizeof(b));
insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b));
return *this;
}
CScript& operator<<(const uint256& b)
{
insert(end(), sizeof(b));
insert(end(), (unsigned char*)&b, (unsigned char*)&b + sizeof(b));
return *this;
}
CScript& operator<<(const CBigNum& b)
{
*this << b.getvch();
return *this;
}
CScript& operator<<(const std::vector<unsigned char>& b)
{
if (b.size() < OP_PUSHDATA1)
{
insert(end(), (unsigned char)b.size());
}
else if (b.size() <= 0xff)
{
insert(end(), OP_PUSHDATA1);
insert(end(), (unsigned char)b.size());
}
else if (b.size() <= 0xffff)
{
insert(end(), OP_PUSHDATA2);
unsigned short nSize = b.size();
insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize));
}
else
{
insert(end(), OP_PUSHDATA4);
unsigned int nSize = b.size();
insert(end(), (unsigned char*)&nSize, (unsigned char*)&nSize + sizeof(nSize));
}
insert(end(), b.begin(), b.end());
return *this;
}
CScript& operator<<(const CScript& b)
{
// I'm not sure if this should push the script or concatenate scripts.
// If there's ever a use for pushing a script onto a script, delete this member fn
assert(!"warning: pushing a CScript onto a CScript with << is probably not intended, use + to concatenate");
return *this;
}
bool GetOp(iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet)
{
// Wrapper so it can be called with either iterator or const_iterator
const_iterator pc2 = pc;
bool fRet = GetOp2(pc2, opcodeRet, &vchRet);
pc = begin() + (pc2 - begin());
return fRet;
}
bool GetOp(iterator& pc, opcodetype& opcodeRet)
{
const_iterator pc2 = pc;
bool fRet = GetOp2(pc2, opcodeRet, NULL);
pc = begin() + (pc2 - begin());
return fRet;
}
bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const
{
return GetOp2(pc, opcodeRet, &vchRet);
}
bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const
{
return GetOp2(pc, opcodeRet, NULL);
}
bool GetOp2(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet) const
{
opcodeRet = OP_INVALIDOPCODE;
if (pvchRet)
pvchRet->clear();
if (pc >= end())
return false;
// Read instruction
if (end() - pc < 1)
return false;
unsigned int opcode = *pc++;
// Immediate operand
if (opcode <= OP_PUSHDATA4)
{
unsigned int nSize;
if (opcode < OP_PUSHDATA1)
{
nSize = opcode;
}
else if (opcode == OP_PUSHDATA1)
{
if (end() - pc < 1)
return false;
nSize = *pc++;
}
else if (opcode == OP_PUSHDATA2)
{
if (end() - pc < 2)
return false;
nSize = 0;
memcpy(&nSize, &pc[0], 2);
pc += 2;
}
else if (opcode == OP_PUSHDATA4)
{
if (end() - pc < 4)
return false;
memcpy(&nSize, &pc[0], 4);
pc += 4;
}
if (end() - pc < nSize)
return false;
if (pvchRet)
pvchRet->assign(pc, pc + nSize);
pc += nSize;
}
opcodeRet = (opcodetype)opcode;
return true;
}
void FindAndDelete(const CScript& b)
{
if (b.empty())
return;
iterator pc = begin();
opcodetype opcode;
do
{
while (end() - pc >= b.size() && memcmp(&pc[0], &b[0], b.size()) == 0)
erase(pc, pc + b.size());
}
while (GetOp(pc, opcode));
}
int GetSigOpCount() const
{
int n = 0;
const_iterator pc = begin();
while (pc < end())
{
opcodetype opcode;
if (!GetOp(pc, opcode))
break;
if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY)
n++;
else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY)
n += 20;
}
return n;
}
bool IsPushOnly() const
{
if (size() > 200)
return false;
const_iterator pc = begin();
while (pc < end())
{
opcodetype opcode;
if (!GetOp(pc, opcode))
return false;
if (opcode > OP_16)
return false;
}
return true;
}
NewcoinAddress GetBitcoinAddress() const
{
opcodetype opcode;
std::vector<unsigned char> vch;
CScript::const_iterator pc = begin();
if (!GetOp(pc, opcode, vch) || opcode != OP_DUP) return 0;
if (!GetOp(pc, opcode, vch) || opcode != OP_HASH160) return 0;
if (!GetOp(pc, opcode, vch) || vch.size() != sizeof(uint160)) return 0;
uint160 hash160 = uint160(vch);
if (!GetOp(pc, opcode, vch) || opcode != OP_EQUALVERIFY) return 0;
if (!GetOp(pc, opcode, vch) || opcode != OP_CHECKSIG) return 0;
if (pc != end()) return 0;
return NewcoinAddress(hash160);
}
void SetBitcoinAddress(const NewcoinAddress& address)
{
this->clear();
*this << OP_DUP << OP_HASH160 << address.GetHash160()) << OP_EQUALVERIFY << OP_CHECKSIG;
}
void SetBitcoinAddress(const std::vector<unsigned char>& vchPubKey)
{
SetBitcoinAddress(NewcoinAddress(vchPubKey));
}
void PrintHex() const
{
printf("CScript(%s)\n", HexStr(begin(), end(), true).c_str());
}
std::string ToString() const
{
std::string str;
opcodetype opcode;
std::vector<unsigned char> vch;
const_iterator pc = begin();
while (pc < end())
{
if (!str.empty())
str += " ";
if (!GetOp(pc, opcode, vch))
{
str += "[error]";
return str;
}
if (0 <= opcode && opcode <= OP_PUSHDATA4)
str += ValueString(vch);
else
str += GetOpName(opcode);
}
return str;
}
void print() const
{
printf("%s\n", ToString().c_str());
}
};
bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
bool IsStandard(const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
bool ExtractAddress(const CScript& scriptPubKey, const CKeyStore* pkeystore, NewcoinAddress& addressRet);
bool SignSignature(const CKeyStore& keystore, const CTransaction& txFrom, CTransaction& txTo, unsigned int nIn, int nHashType=SIGHASH_ALL, CScript scriptPrereq=CScript());
bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, int nHashType=0);
#endif

25
src/types.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef __TYPES_HH__
#define __TYPES_HH__
#if defined(_MSC_VER) || defined(__BORLANDC__)
typedef __int64 int64;
typedef unsigned __int64 uint64;
typedef unsigned int uint32;
typedef unsigned short int uint16;
typedef int int32;
#else
typedef long long int64;
typedef unsigned long long uint64;
typedef unsigned int uint32;
typedef unsigned short int uint16;
typedef int int32;
#endif
#if defined(_MSC_VER) && _MSC_VER < 1300
#define for if (false) ; else for
#endif
#endif

794
src/uint256.h Normal file
View File

@@ -0,0 +1,794 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2011 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
#ifndef NEWCOIN_UINT256_H
#define NEWCOIN_UINT256_H
#include <climits>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include "types.h"
#if defined(_MSC_VER) && _MSC_VER < 1300
#define for if (false) ; else for
#endif
// These classes all store their values internally
// in little-endian form
inline int Testuint256AdHoc(std::vector<std::string> vArg);
// We have to keep a separate base class without constructors
// so the compiler will let us use it in a union
template<unsigned int BITS>
class base_uint
{
protected:
enum { WIDTH=BITS/32 };
unsigned int pn[WIDTH];
public:
bool isZero() const
{
for (int i = 0; i < WIDTH; i++)
if (pn[i] != 0)
return false;
return true;
}
bool isNonZero() const
{
return !isZero();
}
bool operator!() const
{
for (int i = 0; i < WIDTH; i++)
if (pn[i] != 0)
return false;
return true;
}
const base_uint operator~() const
{
base_uint ret;
for (int i = 0; i < WIDTH; i++)
ret.pn[i] = ~pn[i];
return ret;
}
const base_uint operator-() const
{
base_uint ret;
for (int i = 0; i < WIDTH; i++)
ret.pn[i] = ~pn[i];
++ret;
return ret;
}
base_uint& operator=(uint64 b)
{
pn[0] = (unsigned int)b;
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
return *this;
}
base_uint& operator^=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] ^= b.pn[i];
return *this;
}
base_uint& operator&=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] &= b.pn[i];
return *this;
}
base_uint& operator|=(const base_uint& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] |= b.pn[i];
return *this;
}
base_uint& operator^=(uint64 b)
{
pn[0] ^= (unsigned int)(b & 0xffffffffu);
pn[1] ^= (unsigned int)(b >> 32);
return *this;
}
base_uint& operator&=(uint64 b)
{
pn[0] &= (unsigned int)(b & 0xffffffffu);
pn[1] &= (unsigned int)(b >> 32);
return *this;
}
base_uint& operator|=(uint64 b)
{
pn[0] |= (unsigned int)(b & 0xffffffffu);
pn[1] |= (unsigned int)(b >> 32);
return *this;
}
base_uint& operator<<=(unsigned int shift)
{
base_uint a(*this);
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
int k = shift / 32;
shift = shift % 32;
for (int i = 0; i < WIDTH; i++)
{
if (i+k+1 < WIDTH && shift != 0)
pn[i+k+1] |= (a.pn[i] >> (32-shift));
if (i+k < WIDTH)
pn[i+k] |= (a.pn[i] << shift);
}
return *this;
}
base_uint& operator>>=(unsigned int shift)
{
base_uint a(*this);
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
int k = shift / 32;
shift = shift % 32;
for (int i = 0; i < WIDTH; i++)
{
if (i-k-1 >= 0 && shift != 0)
pn[i-k-1] |= (a.pn[i] << (32-shift));
if (i-k >= 0)
pn[i-k] |= (a.pn[i] >> shift);
}
return *this;
}
base_uint& operator+=(const base_uint& b)
{
uint64 carry = 0;
for (int i = 0; i < WIDTH; i++)
{
uint64 n = carry + pn[i] + b.pn[i];
pn[i] = n & 0xffffffff;
carry = n >> 32;
}
return *this;
}
base_uint& operator-=(const base_uint& b)
{
*this += -b;
return *this;
}
base_uint& operator+=(uint64 b64)
{
base_uint b(b64);
*this += b;
return *this;
}
base_uint& operator-=(uint64 b64)
{
base_uint b(b64);
*this += -b;
return *this;
}
base_uint& operator++()
{
// prefix operator
int i = 0;
while (++pn[i] == 0 && i < WIDTH-1)
i++;
return *this;
}
const base_uint operator++(int)
{
// postfix operator
const base_uint ret = *this;
++(*this);
return ret;
}
base_uint& operator--()
{
// prefix operator
unsigned i = 0;
while (--pn[i] == -1 && i < WIDTH-1)
i++;
return *this;
}
const base_uint operator--(int)
{
// postfix operator
const base_uint ret = *this;
--(*this);
return ret;
}
friend inline bool operator<(const base_uint& a, const base_uint& b)
{
for (int i = base_uint::WIDTH-1; i >= 0; i--)
{
if (a.pn[i] < b.pn[i])
return true;
else if (a.pn[i] > b.pn[i])
return false;
}
return false;
}
friend inline bool operator<=(const base_uint& a, const base_uint& b)
{
for (int i = base_uint::WIDTH-1; i >= 0; i--)
{
if (a.pn[i] < b.pn[i])
return true;
else if (a.pn[i] > b.pn[i])
return false;
}
return true;
}
friend inline bool operator>(const base_uint& a, const base_uint& b)
{
for (int i = base_uint::WIDTH-1; i >= 0; i--)
{
if (a.pn[i] > b.pn[i])
return true;
else if (a.pn[i] < b.pn[i])
return false;
}
return false;
}
friend inline bool operator>=(const base_uint& a, const base_uint& b)
{
for (int i = base_uint::WIDTH-1; i >= 0; i--)
{
if (a.pn[i] > b.pn[i])
return true;
else if (a.pn[i] < b.pn[i])
return false;
}
return true;
}
friend inline bool operator==(const base_uint& a, const base_uint& b)
{
for (int i = 0; i < base_uint::WIDTH; i++)
if (a.pn[i] != b.pn[i])
return false;
return true;
}
friend inline bool operator==(const base_uint& a, uint64 b)
{
if (a.pn[0] != (unsigned int)(b & 0xffffffffu))
return false;
if (a.pn[1] != (unsigned int)(b >> 32))
return false;
for (int i = 2; i < base_uint::WIDTH; i++)
if (a.pn[i] != 0)
return false;
return true;
}
friend inline bool operator!=(const base_uint& a, const base_uint& b)
{
return (!(a == b));
}
friend inline bool operator!=(const base_uint& a, uint64 b)
{
return (!(a == b));
}
unsigned int GetAt(int j) const
{
return pn[j];
}
unsigned int& PeekAt(int j)
{
return pn[j];
}
std::string GetHex() const
{
char psz[sizeof(pn)*2 + 1];
for (int i = 0; i < sizeof(pn); i++)
sprintf(psz + i*2, "%02X", ((unsigned char*)pn)[sizeof(pn) - i - 1]);
return std::string(psz, psz + sizeof(pn)*2);
}
void SetHex(const char* psz)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
// skip leading spaces
while (isspace(*psz))
psz++;
// skip 0x
if (psz[0] == '0' && tolower(psz[1]) == 'x')
psz += 2;
// hex string to uint
static char phexdigit[256] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0xa,0xb,0xc,0xd,0xe,0xf,0,0,0,0,0,0,0,0,0 };
const char* pbegin = psz;
while (phexdigit[(int) *psz] || *psz == '0')
psz++;
psz--;
unsigned char* p1 = (unsigned char*)pn;
unsigned char* pend = p1 + WIDTH * 4;
while (psz >= pbegin && p1 < pend)
{
*p1 = phexdigit[(unsigned char)*psz--];
if (psz >= pbegin)
{
*p1 |= (phexdigit[(unsigned char)*psz--] << 4);
p1++;
}
}
}
void SetHex(const std::string& str)
{
SetHex(str.c_str());
}
std::string ToString() const
{
return (GetHex());
}
unsigned char* begin()
{
return (unsigned char*)&pn[0];
}
unsigned char* end()
{
return (unsigned char*)&pn[WIDTH];
}
const unsigned char* begin() const
{
return (const unsigned char*)&pn[0];
}
const unsigned char* end() const
{
return (unsigned char*)&pn[WIDTH];
}
unsigned int size()
{
return sizeof(pn);
}
void zero()
{
memset(&pn[0], 0, sizeof(pn));
}
unsigned int GetSerializeSize(int nType=0) const
{
return sizeof(pn);
}
template<typename Stream>
void Serialize(Stream& s, int nType=0) const
{
s.write((char*)pn, sizeof(pn));
}
template<typename Stream>
void Unserialize(Stream& s, int nType=0)
{
s.read((char*)pn, sizeof(pn));
}
friend class uint160;
friend class uint256;
friend inline int Testuint256AdHoc(std::vector<std::string> vArg);
};
typedef base_uint<160> base_uint160;
typedef base_uint<256> base_uint256;
//
// uint160 and uint256 could be implemented as templates, but to keep
// compile errors and debugging cleaner, they're copy and pasted.
//
//////////////////////////////////////////////////////////////////////////////
//
// uint160
//
class uint160 : public base_uint160
{
public:
typedef base_uint160 basetype;
uint160()
{
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
}
uint160(const basetype& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = b.pn[i];
}
uint160& operator=(const basetype& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = b.pn[i];
return *this;
}
uint160(uint64 b)
{
pn[0] = (unsigned int)(b & 0xffffffffu);
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
}
uint160& operator=(uint64 b)
{
pn[0] = (unsigned int)(b & 0xffffffffu);
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
return *this;
}
explicit uint160(const std::string& str)
{
SetHex(str);
}
explicit uint160(const std::vector<unsigned char>& vch)
{
if (vch.size() == sizeof(pn))
memcpy(pn, &vch[0], sizeof(pn));
else
*this = 0;
}
base_uint256 to256() const;
};
inline bool operator==(const uint160& a, uint64 b) { return (base_uint160)a == b; }
inline bool operator!=(const uint160& a, uint64 b) { return (base_uint160)a != b; }
inline const uint160 operator<<(const base_uint160& a, unsigned int shift) { return uint160(a) <<= shift; }
inline const uint160 operator>>(const base_uint160& a, unsigned int shift) { return uint160(a) >>= shift; }
inline const uint160 operator<<(const uint160& a, unsigned int shift) { return uint160(a) <<= shift; }
inline const uint160 operator>>(const uint160& a, unsigned int shift) { return uint160(a) >>= shift; }
inline const uint160 operator^(const base_uint160& a, const base_uint160& b) { return uint160(a) ^= b; }
inline const uint160 operator&(const base_uint160& a, const base_uint160& b) { return uint160(a) &= b; }
inline const uint160 operator|(const base_uint160& a, const base_uint160& b) { return uint160(a) |= b; }
inline const uint160 operator+(const base_uint160& a, const base_uint160& b) { return uint160(a) += b; }
inline const uint160 operator-(const base_uint160& a, const base_uint160& b) { return uint160(a) -= b; }
inline bool operator<(const base_uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; }
inline bool operator<=(const base_uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; }
inline bool operator>(const base_uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; }
inline bool operator>=(const base_uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; }
inline bool operator==(const base_uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; }
inline bool operator!=(const base_uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; }
inline const uint160 operator^(const base_uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; }
inline const uint160 operator&(const base_uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; }
inline const uint160 operator|(const base_uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; }
inline const uint160 operator+(const base_uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; }
inline const uint160 operator-(const base_uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; }
inline bool operator<(const uint160& a, const base_uint160& b) { return (base_uint160)a < (base_uint160)b; }
inline bool operator<=(const uint160& a, const base_uint160& b) { return (base_uint160)a <= (base_uint160)b; }
inline bool operator>(const uint160& a, const base_uint160& b) { return (base_uint160)a > (base_uint160)b; }
inline bool operator>=(const uint160& a, const base_uint160& b) { return (base_uint160)a >= (base_uint160)b; }
inline bool operator==(const uint160& a, const base_uint160& b) { return (base_uint160)a == (base_uint160)b; }
inline bool operator!=(const uint160& a, const base_uint160& b) { return (base_uint160)a != (base_uint160)b; }
inline const uint160 operator^(const uint160& a, const base_uint160& b) { return (base_uint160)a ^ (base_uint160)b; }
inline const uint160 operator&(const uint160& a, const base_uint160& b) { return (base_uint160)a & (base_uint160)b; }
inline const uint160 operator|(const uint160& a, const base_uint160& b) { return (base_uint160)a | (base_uint160)b; }
inline const uint160 operator+(const uint160& a, const base_uint160& b) { return (base_uint160)a + (base_uint160)b; }
inline const uint160 operator-(const uint160& a, const base_uint160& b) { return (base_uint160)a - (base_uint160)b; }
inline bool operator<(const uint160& a, const uint160& b) { return (base_uint160)a < (base_uint160)b; }
inline bool operator<=(const uint160& a, const uint160& b) { return (base_uint160)a <= (base_uint160)b; }
inline bool operator>(const uint160& a, const uint160& b) { return (base_uint160)a > (base_uint160)b; }
inline bool operator>=(const uint160& a, const uint160& b) { return (base_uint160)a >= (base_uint160)b; }
inline bool operator==(const uint160& a, const uint160& b) { return (base_uint160)a == (base_uint160)b; }
inline bool operator!=(const uint160& a, const uint160& b) { return (base_uint160)a != (base_uint160)b; }
inline const uint160 operator^(const uint160& a, const uint160& b) { return (base_uint160)a ^ (base_uint160)b; }
inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; }
inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; }
inline const uint160 operator+(const uint160& a, const uint160& b) { return (base_uint160)a + (base_uint160)b; }
inline const uint160 operator-(const uint160& a, const uint160& b) { return (base_uint160)a - (base_uint160)b; }
//////////////////////////////////////////////////////////////////////////////
//
// uint256
//
class uint256 : public base_uint256
{
public:
typedef base_uint256 basetype;
uint256()
{
for (int i = 0; i < WIDTH; i++)
pn[i] = 0;
}
uint256(const basetype& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = b.pn[i];
}
uint256& operator=(const basetype& b)
{
for (int i = 0; i < WIDTH; i++)
pn[i] = b.pn[i];
return *this;
}
uint256(uint64 b)
{
pn[0] = (unsigned int)(b & 0xffffffff);
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
}
uint256& operator=(uint64 b)
{
pn[0] = (unsigned int)(b & 0xffffffff);
pn[1] = (unsigned int)(b >> 32);
for (int i = 2; i < WIDTH; i++)
pn[i] = 0;
return *this;
}
explicit uint256(const std::string& str)
{
SetHex(str);
}
explicit uint256(const std::vector<unsigned char>& vch)
{
if (vch.size() == sizeof(pn))
memcpy(pn, &vch[0], sizeof(pn));
else
*this = 0;
}
base_uint160 to160() const;
};
inline bool operator==(const uint256& a, uint64 b) { return (base_uint256)a == b; }
inline bool operator!=(const uint256& a, uint64 b) { return (base_uint256)a != b; }
inline const uint256 operator<<(const base_uint256& a, unsigned int shift) { return uint256(a) <<= shift; }
inline const uint256 operator>>(const base_uint256& a, unsigned int shift) { return uint256(a) >>= shift; }
inline const uint256 operator<<(const uint256& a, unsigned int shift) { return uint256(a) <<= shift; }
inline const uint256 operator>>(const uint256& a, unsigned int shift) { return uint256(a) >>= shift; }
inline const uint256 operator^(const base_uint256& a, const base_uint256& b) { return uint256(a) ^= b; }
inline const uint256 operator&(const base_uint256& a, const base_uint256& b) { return uint256(a) &= b; }
inline const uint256 operator|(const base_uint256& a, const base_uint256& b) { return uint256(a) |= b; }
inline const uint256 operator+(const base_uint256& a, const base_uint256& b) { return uint256(a) += b; }
inline const uint256 operator-(const base_uint256& a, const base_uint256& b) { return uint256(a) -= b; }
inline bool operator<(const base_uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; }
inline bool operator<=(const base_uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; }
inline bool operator>(const base_uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; }
inline bool operator>=(const base_uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; }
inline bool operator==(const base_uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; }
inline bool operator!=(const base_uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; }
inline const uint256 operator^(const base_uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; }
inline const uint256 operator&(const base_uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; }
inline const uint256 operator|(const base_uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; }
inline const uint256 operator+(const base_uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; }
inline const uint256 operator-(const base_uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; }
inline bool operator<(const uint256& a, const base_uint256& b) { return (base_uint256)a < (base_uint256)b; }
inline bool operator<=(const uint256& a, const base_uint256& b) { return (base_uint256)a <= (base_uint256)b; }
inline bool operator>(const uint256& a, const base_uint256& b) { return (base_uint256)a > (base_uint256)b; }
inline bool operator>=(const uint256& a, const base_uint256& b) { return (base_uint256)a >= (base_uint256)b; }
inline bool operator==(const uint256& a, const base_uint256& b) { return (base_uint256)a == (base_uint256)b; }
inline bool operator!=(const uint256& a, const base_uint256& b) { return (base_uint256)a != (base_uint256)b; }
inline const uint256 operator^(const uint256& a, const base_uint256& b) { return (base_uint256)a ^ (base_uint256)b; }
inline const uint256 operator&(const uint256& a, const base_uint256& b) { return (base_uint256)a & (base_uint256)b; }
inline const uint256 operator|(const uint256& a, const base_uint256& b) { return (base_uint256)a | (base_uint256)b; }
inline const uint256 operator+(const uint256& a, const base_uint256& b) { return (base_uint256)a + (base_uint256)b; }
inline const uint256 operator-(const uint256& a, const base_uint256& b) { return (base_uint256)a - (base_uint256)b; }
inline bool operator<(const uint256& a, const uint256& b) { return (base_uint256)a < (base_uint256)b; }
inline bool operator<=(const uint256& a, const uint256& b) { return (base_uint256)a <= (base_uint256)b; }
inline bool operator>(const uint256& a, const uint256& b) { return (base_uint256)a > (base_uint256)b; }
inline bool operator>=(const uint256& a, const uint256& b) { return (base_uint256)a >= (base_uint256)b; }
inline bool operator==(const uint256& a, const uint256& b) { return (base_uint256)a == (base_uint256)b; }
inline bool operator!=(const uint256& a, const uint256& b) { return (base_uint256)a != (base_uint256)b; }
inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; }
inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; }
inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; }
inline const uint256 operator+(const uint256& a, const uint256& b) { return (base_uint256)a + (base_uint256)b; }
inline const uint256 operator-(const uint256& a, const uint256& b) { return (base_uint256)a - (base_uint256)b; }
inline int Testuint256AdHoc(std::vector<std::string> vArg)
{
uint256 g(0);
printf("%s\n", g.ToString().c_str());
--g; printf("--g\n");
printf("%s\n", g.ToString().c_str());
g--; printf("g--\n");
printf("%s\n", g.ToString().c_str());
g++; printf("g++\n");
printf("%s\n", g.ToString().c_str());
++g; printf("++g\n");
printf("%s\n", g.ToString().c_str());
g++; printf("g++\n");
printf("%s\n", g.ToString().c_str());
++g; printf("++g\n");
printf("%s\n", g.ToString().c_str());
uint256 a(7);
printf("a=7\n");
printf("%s\n", a.ToString().c_str());
uint256 b;
printf("b undefined\n");
printf("%s\n", b.ToString().c_str());
int c = 3;
a = c;
a.pn[3] = 15;
printf("%s\n", a.ToString().c_str());
uint256 k(c);
a = 5;
a.pn[3] = 15;
printf("%s\n", a.ToString().c_str());
b = 1;
b <<= 52;
a |= b;
a ^= 0x500;
printf("a %s\n", a.ToString().c_str());
a = a | b | (uint256)0x1000;
printf("a %s\n", a.ToString().c_str());
printf("b %s\n", b.ToString().c_str());
a = 0xfffffffe;
a.pn[4] = 9;
printf("%s\n", a.ToString().c_str());
a++;
printf("%s\n", a.ToString().c_str());
a++;
printf("%s\n", a.ToString().c_str());
a++;
printf("%s\n", a.ToString().c_str());
a++;
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
uint256 d = a--;
printf("%s\n", d.ToString().c_str());
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
a--;
printf("%s\n", a.ToString().c_str());
d = a;
printf("%s\n", d.ToString().c_str());
for (int i = uint256::WIDTH-1; i >= 0; i--) printf("%08x", d.pn[i]); printf("\n");
uint256 neg = d;
neg = ~neg;
printf("%s\n", neg.ToString().c_str());
uint256 e = uint256("0xABCDEF123abcdef12345678909832180000011111111");
printf("\n");
printf("%s\n", e.ToString().c_str());
printf("\n");
uint256 x1 = uint256("0xABCDEF123abcdef12345678909832180000011111111");
uint256 x2;
printf("%s\n", x1.ToString().c_str());
for (int i = 0; i < 270; i += 4)
{
x2 = x1 << i;
printf("%s\n", x2.ToString().c_str());
}
printf("\n");
printf("%s\n", x1.ToString().c_str());
for (int i = 0; i < 270; i += 4)
{
x2 = x1;
x2 >>= i;
printf("%s\n", x2.ToString().c_str());
}
for (int i = 0; i < 100; i++)
{
uint256 k = (~uint256(0) >> i);
printf("%s\n", k.ToString().c_str());
}
for (int i = 0; i < 100; i++)
{
uint256 k = (~uint256(0) << i);
printf("%s\n", k.ToString().c_str());
}
return (0);
}
#endif