diff --git a/Application.h b/Application.h index 9faa98a0e0..47d27f213d 100644 --- a/Application.h +++ b/Application.h @@ -5,6 +5,9 @@ #include "TimingService.h" #include "ValidationCollection.h" #include "Wallet.h" +#include "Serializer.h" +#include "database/database.h" + #include class RPCDoor; @@ -18,12 +21,14 @@ class Application KnownNodeList mKnownNodes; Wallet mWallet; ValidationCollection mValidations; + Database* mDatabase; LedgerMaster mLedgerMaster; ConnectionPool mConnectionPool; PeerDoor* mPeerDoor; RPCDoor* mRPCDoor; + Serializer* mSerializer; boost::asio::io_service mIOService; @@ -37,6 +42,12 @@ public: UniqueNodeList& getUNL(){ return(mUNL); } ValidationCollection& getValidationCollection(){ return(mValidations); } Wallet& getWallet(){ return(mWallet); } + Database* getDB(){ return(mDatabase); } + + void setDatabase(Database* db){ mDatabase=db; } + + Serializer* getSerializer(){ return(mSerializer); } + void setSerializer(Serializer* ser){ mSerializer=ser; } void run(); diff --git a/Config.cpp b/Config.cpp index 9f742387f9..f2d146203b 100644 --- a/Config.cpp +++ b/Config.cpp @@ -1,5 +1,8 @@ #include "Config.h" #include "util/pugixml.hpp" +#include "Application.h" +#include "database/SqliteDatabase.h" + #include using namespace pugi; @@ -50,5 +53,14 @@ void Config::load() node= root.child("RPC_PORT"); if(!node.empty()) RPC_PORT=boost::lexical_cast(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 */ + theApp->setDB(new SqliteDatabase()); + } \ No newline at end of file diff --git a/Config.h b/Config.h index d2f9d90047..199ba15a3a 100644 --- a/Config.h +++ b/Config.h @@ -28,6 +28,8 @@ public: + + Config(); void load(); diff --git a/Debug/newcoin.exe.embed.manifest.res b/Debug/newcoin.exe.embed.manifest.res index c4d02afa23..9c8df0e3c8 100644 Binary files a/Debug/newcoin.exe.embed.manifest.res and b/Debug/newcoin.exe.embed.manifest.res differ diff --git a/Ledger.cpp b/Ledger.cpp index d04f3b5a19..702e26ae59 100644 --- a/Ledger.cpp +++ b/Ledger.cpp @@ -11,6 +11,13 @@ using namespace boost; using namespace std; +Ledger::Ledger() +{ + mIndex=0; + mValidSig=false; + mValidHash=false; + mValidationSeqNum=0; +} Ledger::Ledger(uint32 index) { @@ -20,6 +27,15 @@ Ledger::Ledger(uint32 index) mValidationSeqNum=0; } +Ledger::Ledger(Ledger::pointer other) +{ + mValidSig=false; + mValidHash=false; + mValidationSeqNum=0; + mIndex=other->getIndex(); + mergeIn(other); +} + Ledger::Ledger(newcoin::FullLedger& ledger) { setTo(ledger); @@ -27,11 +43,11 @@ Ledger::Ledger(newcoin::FullLedger& ledger) // TODO: we should probably make a shared pointer type for each of these PB types newcoin::FullLedger* Ledger::createFullLedger() -{ - // TODO: do we need to hash and create accounts map first? +{ newcoin::FullLedger* ledger=new newcoin::FullLedger(); ledger->set_index(mIndex); - ledger->set_hash(mHash.begin(),mHash.GetSerializeSize()); + ledger->set_hash(getHash().begin(),getHash().GetSerializeSize()); + ledger->set_parenthash(mParentHash.begin(),mParentHash.GetSerializeSize()); pair >& account=pair >(); BOOST_FOREACH(account,mAccounts) @@ -41,7 +57,6 @@ newcoin::FullLedger* Ledger::createFullLedger() saveAccount->set_amount(account.second.first); saveAccount->set_seqnum(account.second.second); } - //mBundle.addTransactionsToPB(ledger); return(ledger); } @@ -81,8 +96,27 @@ Ledger::pointer Ledger::getParent() return(mParent); } +bool Ledger::load(uint256& hash) +{ + Database* db=theApp->getDB(); + + string sql="SELECT * from Ledgers where hash="; + string hashStr; + db->escape(hash.begin(),hash.GetSerializeSize(),hashStr); + sql.append(hashStr); + + if(db->executeSQL(sql.c_str())) + { + db->getNextRow(); + sql="SELECT * from Transactions where " + } + return(false); +} + +/* bool Ledger::load(std::string dir) { + string filename=strprintf("%s%u.ledger",dir,mIndex); ifstream loadfile(filename, ios::in | ios::binary); @@ -112,6 +146,7 @@ void Ledger::save(string dir) } delete(ledger); } +*/ int64 Ledger::getAmountHeld(uint160& address) { @@ -151,13 +186,13 @@ void Ledger::publishValidation() void Ledger::sign() { - // TODO: + // TODO: Ledger::sign() } void Ledger::hash() { - // TODO: + // TODO: Ledger::hash() } /* diff --git a/Ledger.h b/Ledger.h index 8919be0375..9b5b75a5d6 100644 --- a/Ledger.h +++ b/Ledger.h @@ -20,7 +20,6 @@ public: private: bool mValidSig; bool mValidHash; - bool mFaith; //TODO: if you will bother to validate this ledger or not. You have to accept the first ledger on Faith uint32 mIndex; uint256 mHash; @@ -49,6 +48,7 @@ private: void correctAccount(uint160& address); public: typedef boost::shared_ptr pointer; + Ledger(); Ledger(uint32 index); Ledger(newcoin::FullLedger& ledger); Ledger(Ledger::pointer other); @@ -56,8 +56,8 @@ public: void setTo(newcoin::FullLedger& ledger); void mergeIn(Ledger::pointer other); - void save(std::string dir); - bool load(std::string dir); + void save(uint256& hash); + bool load(uint256& hash); void recalculate(bool recursive=true); diff --git a/LedgerHistory.cpp b/LedgerHistory.cpp index e1ac7ed5bb..669b3711bd 100644 --- a/LedgerHistory.cpp +++ b/LedgerHistory.cpp @@ -1,6 +1,7 @@ #include "LedgerHistory.h" #include "Config.h" - +#include "Application.h" +#include /* Soon we should support saving the ledger in a real DB For now save them all in @@ -14,16 +15,33 @@ void LedgerHistory::load() } -bool LedgerHistory::loadLedger(uint32 index) +bool LedgerHistory::loadLedger(uint256& hash) { - Ledger::pointer ledger(new Ledger(index)); - if(ledger->load(theConfig.HISTORY_DIR)) + Ledger::pointer ledger=Ledger::pointer(new Ledger()); + if(ledger->load(hash)) { - mAcceptedLedgers[index]=ledger; + mAllLedgers[hash]=ledger; + return(true); } return(false); } +bool LedgerHistory::loadAcceptedLedger(uint32 index) +{ + Ledger::pointer ledger=theApp->getSerializer()->loadAcceptedLedger(index); + if(ledger) + { + mAcceptedLedgers[index]=ledger; + return(true); + } + return(false); +} + +void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger) +{ + mAcceptedLedgers[ledger->getIndex()]=ledger; +} + // this will see if the ledger is in memory // if not it will check disk and load it // if not it will return NULL @@ -31,7 +49,7 @@ Ledger::pointer LedgerHistory::getAcceptedLedger(uint32 index) { if(mAcceptedLedgers.count(index)) return(mAcceptedLedgers[index]); - if(loadLedger(index)) return(mAcceptedLedgers[index]); + if(loadAcceptedLedger(index)) return(mAcceptedLedgers[index]); return(Ledger::pointer()); } @@ -39,4 +57,12 @@ void LedgerHistory::addLedger(Ledger::pointer ledger) { mAcceptedLedgers[ledger->getIndex()]=ledger; ledger->save(theConfig.HISTORY_DIR); -} \ No newline at end of file +} + +Ledger::pointer LedgerHistory::getLedger(uint256& hash) +{ + if(mAllLedgers.count(hash)) + return(mAllLedgers[hash]); + if(loadLedger(hash)) return(mAllLedgers[hash]); + return(Ledger::pointer()); +} diff --git a/LedgerHistory.h b/LedgerHistory.h index 8a113c9a5a..ff8c6d507a 100644 --- a/LedgerHistory.h +++ b/LedgerHistory.h @@ -18,7 +18,8 @@ class LedgerHistory std::map mAcceptedLedgers; std::map mAllLedgers; - bool loadLedger(uint32 index); + bool loadAcceptedLedger(uint32 index); + bool loadLedger(uint256& hash); public: void load(); diff --git a/LedgerMaster.cpp b/LedgerMaster.cpp index 88859d2241..c6d643a098 100644 --- a/LedgerMaster.cpp +++ b/LedgerMaster.cpp @@ -225,14 +225,15 @@ void LedgerMaster::checkLedgerProposal(Peer::pointer peer, newcoin::ProposeLedge addFutureProposal(peer,otherLedger); }else { // you guys are on the same page - if(mFinalizingLedger->getHash()!= Transaction::protobufToInternalHash(otherLedger.hash())) + uint256 otherHash=Transaction::protobufToInternalHash(otherLedger.hash()); + if(mFinalizingLedger->getHash()!= otherHash) { if( mFinalizingLedger->getNumTransactions()>=otherLedger.numtransactions()) { peer->sendLedgerProposal(mFinalizingLedger); }else { - peer->sendGetFullLedger(otherLedger.ledgerindex()); + peer->sendGetFullLedger(otherHash); } } } diff --git a/NewcoinAddress.cpp b/NewcoinAddress.cpp index 9e8dc4c11e..058e1d3cf3 100644 --- a/NewcoinAddress.cpp +++ b/NewcoinAddress.cpp @@ -3,6 +3,22 @@ #include "BitcoinUtil.h" #include + + +uint160 NewcoinAddress::protobufToInternal(const std::string& buf) +{ + uint160 ret; + // TODO: + return(ret); +} + +uint160 NewcoinAddress::humanToInternal(const std::string& buf) +{ + uint160 ret; + // TODO: + return(ret); +} + bool NewcoinAddress::SetHash160(const uint160& hash160) { SetData(theConfig.TEST_NET ? 112 : 1, &hash160, 20); diff --git a/Peer.cpp b/Peer.cpp index 2d8def08b5..7d2043fb2c 100644 --- a/Peer.cpp +++ b/Peer.cpp @@ -134,6 +134,15 @@ PackedMessage::pointer Peer::createValidation(Ledger::pointer ledger) return(packet); } +PackedMessage::pointer Peer::createGetFullLedger(uint256& hash) +{ + newcoin::GetFullLedger* gfl=new newcoin::GetFullLedger(); + gfl->set_hash(hash.begin(),hash.GetSerializeSize()); + + PackedMessage::pointer packet(new PackedMessage(PackedMessage::MessagePointer(gfl),newcoin::GET_FULL_LEDGER)); + return(packet); +} + void Peer::sendLedgerProposal(Ledger::pointer ledger) { PackedMessage::pointer packet=Peer::createLedgerProposal(ledger); @@ -150,12 +159,9 @@ void Peer::sendFullLedger(Ledger::pointer ledger) } } -void Peer::sendGetFullLedger(uint32 index) +void Peer::sendGetFullLedger(uint256& hash) { - newcoin::GetFullLedger* gfl=new newcoin::GetFullLedger(); - gfl->set_ledgerindex(index); - - PackedMessage::pointer packet(new PackedMessage(PackedMessage::MessagePointer(gfl),newcoin::GET_FULL_LEDGER)); + PackedMessage::pointer packet=createGetFullLedger(hash); sendPacket(packet); } diff --git a/Peer.h b/Peer.h index 3b2ead7b52..fc70309230 100644 --- a/Peer.h +++ b/Peer.h @@ -79,7 +79,7 @@ public: void sendPacket(PackedMessage::pointer packet); void sendLedgerProposal(Ledger::pointer ledger); void sendFullLedger(Ledger::pointer ledger); - void sendGetFullLedger(uint32 index); + void sendGetFullLedger(uint256& hash); //static PackedMessage::pointer createFullLedger(Ledger::pointer ledger); static PackedMessage::pointer createLedgerProposal(Ledger::pointer ledger); diff --git a/Transaction.cpp b/Transaction.cpp index 8f514ab8b9..0c5bfc3a6c 100644 --- a/Transaction.cpp +++ b/Transaction.cpp @@ -32,4 +32,17 @@ uint256 Transaction::calcHash(TransactionPtr trans) buffer.resize(trans->ByteSize()); trans->SerializeToArray(&(buffer[0]),buffer.size()); return Hash(buffer.begin(), buffer.end()); +} + +uint256 Transaction::protobufToInternalHash(const std::string& hash) +{ + uint256 ret; + // TODO: + return(ret); +} + +bool Transaction::isSigValid(TransactionPtr trans) +{ + // TODO: + return(true); } \ No newline at end of file diff --git a/UniqueNodeList.cpp b/UniqueNodeList.cpp index 2da29d2441..696cb87d0c 100644 --- a/UniqueNodeList.cpp +++ b/UniqueNodeList.cpp @@ -1,7 +1,31 @@ #include "UniqueNodeList.h" +#include "Application.h" -void UniqueNodeList::load() + +void UniqueNodeList::addNode(uint160& hanko,uint1024& publicKey) { + "INSERT INTO UNL values (" + theApp->getDB()->executeSQL(sql); +} + +void UniqueNodeList::removeNode(uint160& hanko) +{ + "DELETE FROM UNL where hanko=" + theApp->getDB()->executeSQL(sql); } +int UniqueNodeList::checkValid(newcoin::Validation& valid) +{ + "SELECT pubkey from UNL where hanko=" + if( theApp->getDB()->executeSQL(sql) ) + { + if(theApp->getDB()->getNextRow()) + { + theApp->getDB()->getBytes(); + + }else return(0); // not on our list + } + return(1); +} + diff --git a/UniqueNodeList.h b/UniqueNodeList.h index 7abc79d48b..91cd2d3e52 100644 --- a/UniqueNodeList.h +++ b/UniqueNodeList.h @@ -4,11 +4,17 @@ class UniqueNodeList { + // hanko to public key + //std::map mUNL; public: - void load(); - void save(); + //void load(); + //void save(); - bool findHanko(const std::string& hanko); + void addNode(uint160& hanko,uint1024& publicKey); + void removeNode(uint160& hanko); + + // 0- we don't care, 1- we care and is valid, 2-invalid signature + int checkValid(newcoin::Validation& valid); }; diff --git a/ValidationCollection.cpp b/ValidationCollection.cpp index f4584392f6..229b3a1126 100644 --- a/ValidationCollection.cpp +++ b/ValidationCollection.cpp @@ -13,7 +13,19 @@ 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() +{ + +} bool ValidationCollection::hasValidation(uint256& ledgerHash,uint160& hanko,uint32 seqnum) { @@ -51,16 +63,20 @@ void ValidationCollection::addValidation(newcoin::Validation& valid) if(hasValidation(hash,hanko,valid.seqnum())) return; // check if we care about this hanko - if( theApp->getUNL().findHanko(valid.hanko()) ) + int validity=theApp->getUNL().checkValid(valid); + if( validity==1 ) { mValidations[hash].push_back(valid); mIndexValidations[valid.ledgerindex()].push_back(valid); addToGroup(valid); theApp->getLedgerMaster().checkConsensus(valid.ledgerindex()); - }else + }else if(validity==0) { mIgnoredValidations[hash].push_back(valid); + }else + { // the signature wasn't valid + cout << "Invalid Validation" << endl; } } diff --git a/database/SqliteDatabase.cpp b/database/SqliteDatabase.cpp new file mode 100644 index 0000000000..8849faa9e6 --- /dev/null +++ b/database/SqliteDatabase.cpp @@ -0,0 +1,23 @@ +#include "SqliteDatabase.h" + +SqliteDatabase::SqliteDatabase() +{ + +} + +/* http://www.sqlite.org/lang_expr.html +BLOB literals are string literals containing hexadecimal data and preceded by a single "x" or "X" character. For example: +X'53514C697465' +*/ +void SqliteDatabase::escape(unsigned char* start,int size,std::string& retStr) +{ + retStr.clear(); + + char buf[3]; + retStr.append("X'"); + for(int n=0; n + + +Database::Database(const char* host,const char* user,const char* pass) : mDBPass(pass), mHost(host) ,mUser(user), mNumCol(0) +{ + mColNameTable=NULL; +} + +Database::~Database() +{ + delete[] mColNameTable; + +} + + +char* Database::getStr(const char* colName,std::string& retStr) +{ + int index; + if(getColNumber(colName,&index)) + { + return(getStr(index,retStr)); + } + return(NULL); +} + +w32 Database::getInt(const char* colName) +{ + int index; + if(getColNumber(colName,&index)) + { + return(getInt(index)); + } + return(0); +} + +float Database::getFloat(const char* colName) +{ + int index; + if(getColNumber(colName,&index)) + { + return(getFloat(index)); + } + return(0); +} + +bool Database::getBool(const char* colName) +{ + int index; + if(getColNumber(colName,&index)) + { + return(getBool(index)); + } + return(0); +} + + + +// returns false if can't find col +bool Database::getColNumber(const char* colName,int* retIndex) +{ + for(int n=0; nstatusMsg("ERROR with database: %s",sql); + ret=0; + } + return(ret); +} + +float Database::getSingleDBValueFloat(const char* sql) +{ + float ret; + if( executeSQL(sql) && startIterRows() && getNextRow()) + { + ret=getFloat(0); + endIterRows(); + }else + { + //theUI->statusMsg("ERROR with database: %s",sql); + ret=0; + } + return(ret); +} + +char* Database::getSingleDBValueStr(const char* sql,std::string& retStr) +{ + char* ret; + if( executeSQL(sql) && startIterRows() && getNextRow()) + { + ret=getStr(0,retStr); + endIterRows(); + }else + { + //theUI->statusMsg("ERROR with database: %s",sql); + ret=0; + } + return(ret); +} \ No newline at end of file diff --git a/database/database.h b/database/database.h new file mode 100644 index 0000000000..600f5f41ca --- /dev/null +++ b/database/database.h @@ -0,0 +1,73 @@ +#ifndef __DATABASE__ +#define __DATABASE__ + +#include + +/* + this maintains the connection to the database +*/ +class Database +{ +protected: + int mNumCol; + std:string mUser; + std:string mHost; + std:string mDBPass; + std:string* mColNameTable; + + bool getColNumber(const char* colName, int* retIndex); + +public: + Database(const char* host,const char* user,const char* pass); + static Database* newMysqlDatabase(const char* host,const char* user,const char* pass); + virtual ~Database(); + + virtual void connect()=0; + virtual void disconnect()=0; + + std::string& getPass(){ return(mDBPass); } + + virtual void escape(unsigned char* start,int size,std::string& retStr)=0; + + // returns true if the query went ok + virtual bool executeSQL(const char* sql)=0; + + // tells you how many rows were changed by an update or insert + virtual int getNumRowsAffected()=0; + + // returns false if there are no results + virtual bool startIterRows()=0; + virtual void endIterRows()=0; + + // call this after you executeSQL + // will return false if there are no more rows + virtual bool getNextRow()=0; + + // get Data from the current row + char* getStr(const char* colName,std::string& retStr); + int32 getInt(const char* colName); + float getFloat(const char* colName); + bool getBool(const char* colName); + bool getBinary(const char* colName,char* buf,int maxSize); + + virtual char* getStr(int colIndex,std::string& retStr)=0; + virtual int32 getInt(int colIndex)=0; + virtual float getFloat(int colIndex)=0; + virtual bool getBool(int colIndex)=0; + virtual bool getBinary(int colIndex,char* buf,int maxSize)=0; + + int getSingleDBValueInt(const char* sql); + float getSingleDBValueFloat(const char* sql); + char* getSingleDBValueStr(const char* sql, std::string& retStr); + +}; + + +class MsqlDatabase +{ + +}; + + +#endif + diff --git a/database/linux/mysqldatabase.cpp b/database/linux/mysqldatabase.cpp new file mode 100644 index 0000000000..4d35b7ccd5 --- /dev/null +++ b/database/linux/mysqldatabase.cpp @@ -0,0 +1,136 @@ +#include "mysqldatabase.h" +#include "reportingmechanism.h" +#include "string/i4string.h" +#include + + +Database* Database::newDatabase(const char* host,const char* user,const char* pass) +{ + return(new MySqlDatabase(host,user,pass)); +} + +MySqlDatabase::MySqlDatabase(const char* host,const char* user,const char* pass) : Database(host,user,pass) +{ + +} + +MySqlDatabase::~MySqlDatabase() +{ +} + +void MySqlDatabase::connect() +{ + mysql_init(&mMysql); + mysql_options(&mMysql,MYSQL_READ_DEFAULT_GROUP,"i4min"); + if(!mysql_real_connect(&mMysql,mHost,mUser,mDBPass,NULL,0,NULL,0)) + { + theUI->statusMsg("Failed to connect to database: Error: %s\n", mysql_error(&mMysql)); + }else theUI->statusMsg("Connection Established to DB"); +} + +void MySqlDatabase::disconnect() +{ + mysql_close(&mMysql); +} + +int MySqlDatabase::getNumRowsAffected() +{ + return( mysql_affected_rows(&mMysql)); +} + +// returns true if the query went ok +bool MySqlDatabase::executeSQL(const char* sql) +{ + int ret=mysql_query(&mMysql, sql); + if(ret) + { + connect(); + int ret=mysql_query(&mMysql, sql); + if(ret) + { + theUI->statusMsg("ERROR with executeSQL: %d %s",ret,sql); + return(false); + } + } + return(true); +} + +bool MySqlDatabase::startIterRows() +{ + mResult=mysql_store_result(&mMysql); + // get total number of columns from the resultset + mNumCol = mysql_num_fields(mResult); + if(mNumCol) + { + delete[](mColNameTable); + mColNameTable=new i4_str[mNumCol]; + + // fill out the column name table + for(int n = 0; n < mNumCol; n++) + { + MYSQL_FIELD* field=mysql_fetch_field(mResult); + mColNameTable[n]= field->name; + } + return(true); + } + return(false); +} + +// call this after you executeSQL +// will return false if there are no more rows +bool MySqlDatabase::getNextRow() +{ + mCurRow=mysql_fetch_row(mResult); + + return(mCurRow!=NULL); +} + +char* MySqlDatabase::getStr(int colIndex,i4_str* retStr) +{ + if(mCurRow[colIndex]) + { + (*retStr)=mCurRow[colIndex]; + }else (*retStr)=""; + + return(*retStr); +} + +w32 MySqlDatabase::getInt(int colIndex) +{ + + if(mCurRow[colIndex]) + { + w32 ret=atoll(mCurRow[colIndex]); + //theUI->statusMsg("getInt: %s,%c,%u",mCurRow[colIndex],mCurRow[colIndex][0],ret); + return(ret); + } + return(0); +} + +float MySqlDatabase::getFloat(int colIndex) +{ + if(mCurRow[colIndex]) + { + float ret=atof(mCurRow[colIndex]); + return(ret); + } + return(0.0); +} + +bool MySqlDatabase::getBool(int colIndex) +{ + if(mCurRow[colIndex]) + { + int ret=atoi(mCurRow[colIndex]); + return(ret); + } + return(false); +} + + +void MySqlDatabase::endIterRows() +{ + mysql_free_result(mResult); +} + + diff --git a/database/linux/mysqldatabase.h b/database/linux/mysqldatabase.h new file mode 100644 index 0000000000..d7e4400123 --- /dev/null +++ b/database/linux/mysqldatabase.h @@ -0,0 +1,47 @@ +#ifndef __MYSQLDATABASE__ +#define __MYSQLDATABASE__ + +#include "../database.h" +#include "string/i4string.h" +#include "mysql.h" + +/* +this maintains the connection to the database +*/ +class MySqlDatabase : public Database +{ + MYSQL mMysql; + MYSQL_RES* mResult; + MYSQL_ROW mCurRow; + +public: + MySqlDatabase(const char* host,const char* user,const char* pass); + ~MySqlDatabase(); + + void connect(); + void disconnect(); + + // returns true if the query went ok + bool executeSQL(const char* sql); + + int getNumRowsAffected(); + + // returns false if there are no results + bool startIterRows(); + void endIterRows(); + + // call this after you executeSQL + // will return false if there are no more rows + bool getNextRow(); + + // get Data from the current row + + char* getStr(int colIndex,i4_str* retStr); + w32 getInt(int colIndex); + float getFloat(int colIndex); + bool getBool(int colIndex); +}; + + +#endif + diff --git a/database/win/dbutility.h b/database/win/dbutility.h new file mode 100644 index 0000000000..5df5c1ec0c --- /dev/null +++ b/database/win/dbutility.h @@ -0,0 +1,82 @@ +#ifndef __TMYODBC_UTILITY_H__ +#define __TMYODBC_UTILITY_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + + +/* STANDARD C HEADERS */ +#include +#include +#include + +/* ODBC HEADERS */ +#include + +#define MAX_NAME_LEN 95 +#define MAX_COLUMNS 255 +#define MAX_ROW_DATA_LEN 255 + + +/* PROTOTYPE */ +void myerror(SQLRETURN rc,SQLSMALLINT htype, SQLHANDLE handle); + +/* UTILITY MACROS */ +#define myenv(henv,r) \ + if ( ((r) != SQL_SUCCESS) ) \ + myerror(r, 1,henv); \ + assert( ((r) == SQL_SUCCESS) || ((r) == SQL_SUCCESS_WITH_INFO) ) + +#define myenv_err(henv,r,rc) \ + if ( rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO ) \ + myerror(rc, 1, henv); \ + assert( r ) + +#define mycon(hdbc,r) \ + if ( ((r) != SQL_SUCCESS) ) \ + myerror(r, 2, hdbc); \ + assert( ((r) == SQL_SUCCESS) || ((r) == SQL_SUCCESS_WITH_INFO) ) + +#define mycon_err(hdbc,r,rc) \ + if ( rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO ) \ + myerror(rc, 2, hdbc); \ + assert( r ) + +#define mystmt(hstmt,r) \ + if ( ((r) != SQL_SUCCESS) ) \ + myerror(r, 3, hstmt); \ + assert( ((r) == SQL_SUCCESS) || ((r) == SQL_SUCCESS_WITH_INFO) ) + +#define mystmt_err(hstmt,r,rc) \ + if ( rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO ) \ + myerror(rc, 3, hstmt); \ + assert( r ) + +/******************************************************** +* MyODBC 3.51 error handler * +*********************************************************/ +void myerror(SQLRETURN rc, SQLSMALLINT htype, SQLHANDLE handle) +{ + SQLRETURN lrc; + + if( rc == SQL_ERROR || rc == SQL_SUCCESS_WITH_INFO ) + { + SQLCHAR szSqlState[6],szErrorMsg[SQL_MAX_MESSAGE_LENGTH]; + SQLINTEGER pfNativeError; + SQLSMALLINT pcbErrorMsg; + + lrc = SQLGetDiagRec(htype, handle,1, + (SQLCHAR *)&szSqlState, + (SQLINTEGER *)&pfNativeError, + (SQLCHAR *)&szErrorMsg, + SQL_MAX_MESSAGE_LENGTH-1, + (SQLSMALLINT *)&pcbErrorMsg); + if(lrc == SQL_SUCCESS || lrc == SQL_SUCCESS_WITH_INFO) + printf("\n [%s][%d:%s]\n",szSqlState,pfNativeError,szErrorMsg); + } +} + + +#endif /* __TMYODBC_UTILITY_H__ */ + diff --git a/database/win/windatabase.cpp b/database/win/windatabase.cpp new file mode 100644 index 0000000000..a0e7a18acf --- /dev/null +++ b/database/win/windatabase.cpp @@ -0,0 +1,212 @@ +#include "windatabase.h" +#include "dbutility.h" + +Database* Database::newMysqlDatabase(const char* host,const char* user,const char* pass) +{ + return(new WinDatabase(host,user,pass)); +} + +WinDatabase::WinDatabase(const char* host,const char* user,const char* pass) : Database(host,user,pass) +{ + +} + +WinDatabase::~WinDatabase() +{ + disconnect(); +} + + +void WinDatabase::connect() +{ + SQLRETURN rc; + + rc = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&henv); + myenv(henv, rc); + + rc = SQLSetEnvAttr(henv,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,0); + myenv(henv, rc); + + rc = SQLAllocHandle(SQL_HANDLE_DBC,henv, &hdbc); + myenv(henv, rc); + + rc = SQLConnect(hdbc, (unsigned char*)(char*) mHost, SQL_NTS, (unsigned char*)(char*) mUser, SQL_NTS, (unsigned char*)(char*) mDBPass, SQL_NTS); + mycon(hdbc, rc); + + rc = SQLSetConnectAttr(hdbc,SQL_ATTR_AUTOCOMMIT,(SQLPOINTER)SQL_AUTOCOMMIT_ON,0); + mycon(hdbc,rc); + + rc = SQLAllocHandle(SQL_HANDLE_STMT,hdbc,&hstmt); + mycon(hdbc,rc); + + //rc = SQLGetInfo(hdbc,SQL_DBMS_NAME,&server_name,40,NULL); + //mycon(hdbc, rc); + + theUI->statusMsg("Connection Established to DB"); +} + +void WinDatabase::disconnect() +{ + SQLRETURN rc; + + rc = SQLFreeStmt(hstmt, SQL_DROP); + mystmt(hstmt,rc); + + rc = SQLDisconnect(hdbc); + mycon(hdbc, rc); + + rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc); + mycon(hdbc, rc); + + rc = SQLFreeHandle(SQL_HANDLE_ENV, henv); + myenv(henv, rc); +} + +int WinDatabase::getNumRowsAffected() +{ +// theUI->statusMsg("getNumRowsAffected()"); + SQLINTEGER ret; + SQLRowCount(hstmt,&ret); + return(ret); +} + +// returns true if the query went ok +bool WinDatabase::executeSQL(const char* sql) +{ + SQLRETURN rc = SQLExecDirect(hstmt,(unsigned char*) sql,SQL_NTS); + if(rc==SQL_ERROR) + { + theUI->errorMsg("Trying to recover from DB error"); + rc = SQLExecDirect(hstmt,(unsigned char*) sql,SQL_NTS); + if(rc==SQL_ERROR) + { + SQLCHAR SqlState[6], /*SQLStmt[100],*/ Msg[SQL_MAX_MESSAGE_LENGTH]; + SQLINTEGER NativeError; + SQLSMALLINT i, MsgLen; + SQLRETURN /*rc1,*/ rc2; + + i = 1; + while ((rc2 = SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError, Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA) + { + theUI->errorMsg("DB ERROR: %s,%d,%s",SqlState,NativeError,Msg); + i++; + } + + return(false); + } + } + + mystmt(hstmt,rc); + + // commit the transaction + rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT); + mycon(hdbc,rc); + + return(true); + +} + +bool WinDatabase::startIterRows() +{ + SQLUINTEGER pcColDef; + SQLCHAR szColName[MAX_NAME_LEN]; + SQLSMALLINT pfSqlType, pcbScale, pfNullable; + SQLSMALLINT numCol; + + /* get total number of columns from the resultset */ + SQLRETURN rc = SQLNumResultCols(hstmt,&numCol); + mystmt(hstmt,rc); + mNumCol=(int)numCol; + + if(mNumCol) + { + delete[](mColNameTable); + mColNameTable=new i4_str[mNumCol]; + + // fill out the column name table + for(int n = 1; n <= mNumCol; n++) + { + rc = SQLDescribeCol(hstmt,n,szColName, MAX_NAME_LEN, NULL, &pfSqlType,&pcColDef,&pcbScale,&pfNullable); + mystmt(hstmt,rc); + + mColNameTable[n-1]= (char*)szColName; + } + return(true); + } + return(false); +} + +// call this after you executeSQL +// will return false if there are no more rows +bool WinDatabase::getNextRow() +{ + SQLRETURN rc = SQLFetch(hstmt); + return((rc==SQL_SUCCESS) || (rc==SQL_SUCCESS_WITH_INFO)); +} + + + +char* WinDatabase::getStr(int colIndex,i4_str* retStr) +{ + colIndex++; + (*retStr)=""; + char buf[1000]; +// SQLINTEGER len; + buf[0]=0; + + while(SQLGetData(hstmt, colIndex, SQL_C_CHAR, &buf, 1000,NULL)!= SQL_NO_DATA) + { + (*retStr) += buf; +// theUI->statusMsg("Win: %s",buf); + } + + //SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_C_CHAR,&buf,30000,&len); + //mystmt(hstmt,rc); + //*retStr=buf; + + //theUI->statusMsg("Win: %s",buf); + + return(*retStr); +} + +w32 WinDatabase::getInt(int colIndex) +{ + colIndex++; + int ret=0; + SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_INTEGER,&ret,sizeof(int),NULL); + mystmt(hstmt,rc); + return(ret); +} + +float WinDatabase::getFloat(int colIndex) +{ + colIndex++; + float ret=0; + SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_C_FLOAT,&ret,sizeof(float),NULL); + mystmt(hstmt,rc); + + return(ret); +} + +bool WinDatabase::getBool(int colIndex) +{ + colIndex++; + char buf[1]; + buf[0]=0; + SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_C_CHAR,&buf,1,NULL); + mystmt(hstmt,rc); + + return(buf[0] != 0); +} + + +void WinDatabase::endIterRows() +{ + // free the statement row bind resources + SQLRETURN rc = SQLFreeStmt(hstmt, SQL_UNBIND); + mystmt(hstmt,rc); + + // free the statement cursor + rc = SQLFreeStmt(hstmt, SQL_CLOSE); + mystmt(hstmt,rc); +} diff --git a/database/win/windatabase.h b/database/win/windatabase.h new file mode 100644 index 0000000000..d507e2a358 --- /dev/null +++ b/database/win/windatabase.h @@ -0,0 +1,55 @@ +#ifndef __WINDATABASE__ +#define __WINDATABASE__ + +#include "../database.h" + + +#ifdef WIN32 +#define _WINSOCKAPI_ // prevent inclusion of winsock.h in windows.h +#include +#include "sql.h" +#endif + +#include "string/i4string.h" + +/* + this maintains the connection to the database +*/ +class WinDatabase : public Database +{ + SQLHENV henv; + SQLHDBC hdbc; + SQLHSTMT hstmt; + +public: + WinDatabase(const char* host,const char* user,const char* pass); + virtual ~WinDatabase(); + + void connect(); + void disconnect(); + + char* getPass(){ return(mDBPass); } + + // returns true if the query went ok + bool executeSQL(const char* sql); + + int getNumRowsAffected(); + + // returns false if there are no results + bool startIterRows(); + void endIterRows(); + + // call this after you executeSQL + // will return false if there are no more rows + bool getNextRow(); + + // get Data from the current row + char* getStr(int colIndex,i4_str* retStr); + w32 getInt(int colIndex); + float getFloat(int colIndex); + bool getBool(int colIndex); +}; + + +#endif + diff --git a/db layout.txt b/db layout.txt new file mode 100644 index 0000000000..9e9c30f850 --- /dev/null +++ b/db layout.txt @@ -0,0 +1,13 @@ +// need to make this compatible with at least SQLite and Mysql + +drop database if exists newcoin; +CREATE DATABASE newcoin; +use newcoin; +CREATE TABLE UNL (Hanko BINARY(20) PRIMARY KEY, PubKey BINARY(128)); +CREATE TABLE Transactions (TransactionID INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, From BINARY(20), Dest BINARY(20), Amount BIGINT UNSIGNED, LedgerIndex INT UNSIGNED, SeqNum INT, PubKey BINARY(128), Sig BINARY(32)); +CREATE TABLE Ledgers (LedgerID INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, LedgerIndex INT UNSIGNED, Hash BINARY(32), ParentHash BINARY(32),FeeHeld BIGINT UNSIGNED); //, Accepted TINYINT +CREATE TABLE Validations(LedgerIndex INT UNSIGNED, Hash BINARY(32), Hanko BINARY(20), SeqNum INT UNSIGNED, Sig BINARY(32), WeCare TINYINT); +CREATE TABLE LedgerTransactionMap (LedgerID INT UNSIGNED, TransactionID INT UNSIGNED, Include TINYINT); +CREATE TABLE LedgerAccountMap(LedgerID INT UNSIGNED,AccountID INT UNSIGNED); +CREATE TABLE Accounts (AccountID INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, Address BINARY(20), Amount BIGINT UNSIGNED, SeqNum INT); +CREATE TABLE AcceptedLedgers (LedgerIndex INT UNSIGNED PRIMARY KEY, LedgerID INT UNSIGNED); diff --git a/newcoin.proto b/newcoin.proto index b0bf3a2dbe..7ac02136af 100644 --- a/newcoin.proto +++ b/newcoin.proto @@ -70,8 +70,7 @@ message FullLedger { message GetFullLedger { - required uint32 ledgerIndex = 1; - optional bytes hash = 2; + required bytes hash = 1; } message GetValidations { diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 6ecbaea4a1..6d7af0a828 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -91,6 +91,10 @@ + + + + @@ -100,6 +104,7 @@ + @@ -139,6 +144,8 @@ + + @@ -168,11 +175,13 @@ + + @@ -186,6 +195,7 @@ ..\protoc-2.4.1-win32\protoc -I=..\newcoin --cpp_out=..\newcoin ..\newcoin\newcoin.proto newcoin.pb.h + Designer diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 2fba4e4424..2e79c244bd 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -132,6 +132,21 @@ Source Files + + Source Files + + + Source Files + + + Header Files\util + + + Header Files\util + + + Header Files\util + @@ -308,6 +323,18 @@ Header Files + + Header Files + + + Header Files\util + + + Header Files\util + + + Header Files + @@ -318,6 +345,7 @@ html + diff --git a/notes.txt b/notes.txt index b8ba6f8220..549dbe515b 100644 --- a/notes.txt +++ b/notes.txt @@ -1,9 +1,10 @@ Dependencies: Boost 1_47 - boost log http://boost-log.sourceforge.net/libs/log/doc/html/log/installation.html + boost log http://boost-log.sourceforge.net/libs/log/doc/html/log/installation.html // this didn't work immediatly so I stopped messing with it but we should probably use it protocol buffers: expects ..\protobuf-2.4.1 and ..\protoc-2.4.1-win32 openssl + sqlite Using: pugixml version 1.0 download new versions at http://pugixml.org/ @@ -49,3 +50,4 @@ Peers are causing: Actualy I think this can all be in one thread. Commands to the app must be done by RPC. + diff --git a/uint256.h b/uint256.h index d2dc0f7327..28a45b7910 100644 --- a/uint256.h +++ b/uint256.h @@ -5,7 +5,6 @@ #ifndef BITCOIN_UINT256_H #define BITCOIN_UINT256_H -//#include "serialize.h" #include #include