mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Move sources to src and build objs in obj.
This commit is contained in:
54
src/AccountState.cpp
Normal file
54
src/AccountState.cpp
Normal 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
59
src/AccountState.h
Normal 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
107
src/Application.cpp
Normal 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
94
src/Application.h
Normal 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
37
src/Avalanche.h
Normal 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
31
src/BinaryFormats.h
Normal 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
107
src/BitcoinUtil.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "BitcoinUtil.h"
|
||||
#include <cstdarg>
|
||||
#include <openssl/rand.h>
|
||||
#include <ctime>
|
||||
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
std::string gFormatStr("v1");
|
||||
|
||||
std::string FormatFullVersion()
|
||||
{
|
||||
return(gFormatStr);
|
||||
}
|
||||
|
||||
|
||||
string strprintf(const char* format, ...)
|
||||
{
|
||||
char buffer[50000];
|
||||
char* p = buffer;
|
||||
int limit = sizeof(buffer);
|
||||
int ret;
|
||||
loop
|
||||
{
|
||||
va_list arg_ptr;
|
||||
va_start(arg_ptr, format);
|
||||
ret = _vsnprintf(p, limit, format, arg_ptr);
|
||||
va_end(arg_ptr);
|
||||
if (ret >= 0 && ret < limit)
|
||||
break;
|
||||
if (p != buffer)
|
||||
delete[] p;
|
||||
limit *= 2;
|
||||
p = new char[limit];
|
||||
if (p == NULL)
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
string str(p, p+ret);
|
||||
if (p != buffer)
|
||||
delete[] p;
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
inline int64 GetPerformanceCounter()
|
||||
{
|
||||
int64 nCounter = 0;
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
|
||||
#else
|
||||
timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
nCounter = t.tv_sec * 1000000 + t.tv_usec;
|
||||
#endif
|
||||
return nCounter;
|
||||
}
|
||||
|
||||
void RandAddSeed()
|
||||
{
|
||||
// Seed with CPU performance counter
|
||||
int64 nCounter = GetPerformanceCounter();
|
||||
RAND_add(&nCounter, sizeof(nCounter), 1.5);
|
||||
memset(&nCounter, 0, sizeof(nCounter));
|
||||
}
|
||||
//
|
||||
// "Never go to sea with two chronometers; take one or three."
|
||||
// Our three time sources are:
|
||||
// - System clock
|
||||
// - Median of other nodes's clocks
|
||||
// - The user (asking the user to fix the system clock if the first two disagree)
|
||||
//
|
||||
int64 GetTime()
|
||||
{
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
void RandAddSeedPerfmon()
|
||||
{
|
||||
RandAddSeed();
|
||||
|
||||
// This can take up to 2 seconds, so only do it every 10 minutes
|
||||
static int64 nLastPerfmon;
|
||||
if (GetTime() < nLastPerfmon + 10 * 60)
|
||||
return;
|
||||
nLastPerfmon = GetTime();
|
||||
|
||||
#ifdef WIN32
|
||||
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
|
||||
// Seed with the entire set of perfmon data
|
||||
unsigned char pdata[250000];
|
||||
memset(pdata, 0, sizeof(pdata));
|
||||
unsigned long nSize = sizeof(pdata);
|
||||
long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
|
||||
RegCloseKey(HKEY_PERFORMANCE_DATA);
|
||||
if (ret == ERROR_SUCCESS)
|
||||
{
|
||||
RAND_add(pdata, nSize, nSize/100.0);
|
||||
memset(pdata, 0, nSize);
|
||||
//printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M", GetTime()).c_str(), nSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
106
src/BitcoinUtil.h
Normal file
106
src/BitcoinUtil.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef __BITCOIN_UTIL__
|
||||
#define __BITCOIN_UTIL__
|
||||
|
||||
// TODO: these things should all go somewhere
|
||||
|
||||
#include <string>
|
||||
#include "types.h"
|
||||
#include "uint256.h"
|
||||
#include <openssl/ripemd.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
std::string strprintf(const char* format, ...);
|
||||
std::string FormatFullVersion();
|
||||
void RandAddSeedPerfmon();
|
||||
|
||||
static const unsigned int MAX_SIZE = 0x02000000;
|
||||
|
||||
#define loop for (;;)
|
||||
#define PAIR(t1, t2) pair<t1, t2>
|
||||
|
||||
#if !defined(WIN32) && !defined(WIN64)
|
||||
#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d)
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T1>
|
||||
inline uint256 SHA256Hash(const T1 pbegin, const T1 pend)
|
||||
{
|
||||
static unsigned char pblank[1];
|
||||
uint256 hash1;
|
||||
SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1);
|
||||
uint256 hash2;
|
||||
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
|
||||
return hash2;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2>
|
||||
inline uint256 SHA256Hash(const T1 p1begin, const T1 p1end,
|
||||
const T2 p2begin, const T2 p2end)
|
||||
{
|
||||
static unsigned char pblank[1];
|
||||
uint256 hash1;
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
|
||||
SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
|
||||
SHA256_Final((unsigned char*)&hash1, &ctx);
|
||||
uint256 hash2;
|
||||
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
|
||||
return hash2;
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, typename T3>
|
||||
inline uint256 SHA256Hash(const T1 p1begin, const T1 p1end,
|
||||
const T2 p2begin, const T2 p2end,
|
||||
const T3 p3begin, const T3 p3end)
|
||||
{
|
||||
static unsigned char pblank[1];
|
||||
uint256 hash1;
|
||||
SHA256_CTX ctx;
|
||||
SHA256_Init(&ctx);
|
||||
SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
|
||||
SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
|
||||
SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0]));
|
||||
SHA256_Final((unsigned char*)&hash1, &ctx);
|
||||
uint256 hash2;
|
||||
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
|
||||
return hash2;
|
||||
}
|
||||
|
||||
inline uint160 Hash160(const std::vector<unsigned char>& vch)
|
||||
{
|
||||
uint256 hash1;
|
||||
SHA256(&vch[0], vch.size(), (unsigned char*)&hash1);
|
||||
uint160 hash2;
|
||||
RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
|
||||
return hash2;
|
||||
}
|
||||
|
||||
/*
|
||||
#ifdef WIN32
|
||||
// This is used to attempt to keep keying material out of swap
|
||||
// Note that VirtualLock does not provide this as a guarantee on Windows,
|
||||
// but, in practice, memory that has been VirtualLock'd almost never gets written to
|
||||
// the pagefile except in rare circumstances where memory is extremely low.
|
||||
#include <windows.h>
|
||||
#define mlock(p, n) VirtualLock((p), (n));
|
||||
#define munlock(p, n) VirtualUnlock((p), (n));
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <limits.h>
|
||||
// This comes from limits.h if it's not defined there set a sane default
|
||||
#ifndef PAGESIZE
|
||||
#include <unistd.h>
|
||||
#define PAGESIZE sysconf(_SC_PAGESIZE)
|
||||
#endif
|
||||
#define mlock(a,b) \
|
||||
mlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\
|
||||
(((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1))))
|
||||
#define munlock(a,b) \
|
||||
munlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\
|
||||
(((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1))))
|
||||
#endif
|
||||
*/
|
||||
|
||||
#endif
|
||||
172
src/CallRPC.cpp
Normal file
172
src/CallRPC.cpp
Normal 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
7
src/CallRPC.h
Normal 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
53
src/Config.cpp
Normal 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
46
src/Config.h
Normal 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
44
src/Confirmation.h
Normal 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
110
src/ConnectionPool.cpp
Normal 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
39
src/ConnectionPool.h
Normal 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
53
src/Conversion.cpp
Normal 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
11
src/Conversion.h
Normal 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
100
src/DBInit.cpp
Normal 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
275
src/DeterministicKeys.cpp
Normal 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
11
src/Hanko.cpp
Normal 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
64
src/Hanko.h
Normal 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
124
src/HashedObject.cpp
Normal 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
46
src/HashedObject.h
Normal 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
241
src/HttpReply.cpp
Normal 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
50
src/HttpReply.h
Normal 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
24
src/HttpRequest.h
Normal 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
38
src/KnownNodeList.cpp
Normal 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
31
src/KnownNodeList.h
Normal 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
587
src/Ledger.cpp
Normal 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
124
src/Ledger.h
Normal 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
308
src/LedgerAcquire.cpp
Normal 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
70
src/LedgerAcquire.h
Normal 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
86
src/LedgerHistory.cpp
Normal 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
23
src/LedgerHistory.h
Normal 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
196
src/LedgerMaster.cpp
Normal 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
65
src/LedgerMaster.h
Normal 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
115
src/LocalAccount.h
Normal 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
56
src/LocalTransaction.cpp
Normal 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
58
src/LocalTransaction.h
Normal 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
131
src/NetworkOPs.cpp
Normal 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
62
src/NetworkOPs.h
Normal 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
27
src/NetworkStatus.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef __NETWORKSTATUS__
|
||||
#define __NETWORKSTATUS__
|
||||
|
||||
struct NSBit
|
||||
{ // a network status bit
|
||||
const char *name, *description;
|
||||
int number;
|
||||
};
|
||||
|
||||
struct NetworkStatus
|
||||
{
|
||||
static const int nsbConnected=0; // connected to the network
|
||||
static const int nsbAccepted=1; // accept this as the real network
|
||||
static const int nsbFastSynching=2; // catching up, skipping transactions
|
||||
static const int nsbSlowSynching=3; // catching up, txn by txn
|
||||
static const int nsbSynched=4; // in synch with the network
|
||||
static const int nsbIdentifiable=5; // not hiding our identity
|
||||
static const int nsbLedgerSync=6; // participating in ledger sync
|
||||
static const int nsbStuck=7; // unable to sync
|
||||
static const int nsbShuttingDown=8; // node is shutting down
|
||||
|
||||
static const int nnbCount=32;
|
||||
std::bitset<nnbCount> nsbValues;
|
||||
std::map<int,NSBit> nsbData;
|
||||
};
|
||||
|
||||
#endif
|
||||
58
src/NewcoinAddress.cpp
Normal file
58
src/NewcoinAddress.cpp
Normal 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
27
src/NewcoinAddress.h
Normal 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
60
src/PackedMessage.cpp
Normal 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
77
src/PackedMessage.h
Normal 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
685
src/Peer.cpp
Normal 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
101
src/Peer.h
Normal 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
36
src/PeerDoor.cpp
Normal 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
21
src/PeerDoor.h
Normal 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
74
src/PubKeyCache.cpp
Normal 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
25
src/PubKeyCache.h
Normal 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
39
src/RPC.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "../json/value.h"
|
||||
|
||||
enum http_status_type
|
||||
{
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
no_content = 204,
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
moved_temporarily = 302,
|
||||
not_modified = 304,
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503
|
||||
};
|
||||
|
||||
extern std::string JSONRPCRequest(const std::string& strMethod, const Json::Value& params,
|
||||
const Json::Value& id);
|
||||
|
||||
extern std::string createHTTPPost(const std::string& strMsg,
|
||||
const std::map<std::string, std::string>& mapRequestHeaders);
|
||||
|
||||
extern int ReadHTTP(std::basic_istream<char>& stream,
|
||||
std::map<std::string, std::string>& mapHeadersRet, std::string& strMessageRet);
|
||||
|
||||
extern std::string HTTPReply(int nStatus, const std::string& strMsg);
|
||||
|
||||
extern std::string JSONRPCReply(const Json::Value& result, const Json::Value& error, const Json::Value& id);
|
||||
|
||||
extern Json::Value JSONRPCError(int code, const std::string& message);
|
||||
0
src/RPCCommands.cpp
Normal file
0
src/RPCCommands.cpp
Normal file
6
src/RPCCommands.h
Normal file
6
src/RPCCommands.h
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
class RPCCommands
|
||||
{
|
||||
public:
|
||||
HttpReply& handleCommand();
|
||||
};
|
||||
49
src/RPCDoor.cpp
Normal file
49
src/RPCDoor.cpp
Normal 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
18
src/RPCDoor.h
Normal 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
537
src/RPCServer.cpp
Normal 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 ¶ms)
|
||||
{ // 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 ¶ms)
|
||||
{ // 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 ¶ms)
|
||||
{ // 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 ¶ms)
|
||||
{ // 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 ¶ms)
|
||||
{
|
||||
// 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
66
src/RPCServer.h
Normal 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
330
src/RequestParser.cpp
Normal 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
72
src/RequestParser.h
Normal 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
679
src/SHAMap.cpp
Normal 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
329
src/SHAMap.h
Normal 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
181
src/SHAMapDiff.cpp
Normal 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
370
src/SHAMapNodes.cpp
Normal 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
496
src/SHAMapSync.cpp
Normal 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
27
src/ScopedLock.h
Normal 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
43
src/SecureAllocator.h
Normal 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
235
src/Serializer.cpp
Normal 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
75
src/Serializer.h
Normal 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
201
src/TaggedCache.h
Normal 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
57
src/TimingService.cpp
Normal 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
26
src/TimingService.h
Normal 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
385
src/Transaction.cpp
Normal 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
118
src/Transaction.h
Normal 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
40
src/TransactionMaster.cpp
Normal 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
24
src/TransactionMaster.h
Normal 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
82
src/UniqueNodeList.cpp
Normal 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
25
src/UniqueNodeList.h
Normal 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
|
||||
234
src/ValidationCollection.cpp
Normal file
234
src/ValidationCollection.cpp
Normal 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);
|
||||
}
|
||||
56
src/ValidationCollection.h
Normal file
56
src/ValidationCollection.h
Normal 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
914
src/Wallet.cpp
Normal 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
92
src/Wallet.h
Normal 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
245
src/base58.h
Normal 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
538
src/bignum.h
Normal 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
278
src/key.h
Normal 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
36
src/keystore.cpp
Normal 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
58
src/keystore.h
Normal 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
63
src/main.cpp
Normal 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
249
src/rpc.cpp
Normal 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
403
src/script.h
Normal 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
25
src/types.h
Normal 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
794
src/uint256.h
Normal 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
|
||||
Reference in New Issue
Block a user