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

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

View File

@@ -0,0 +1,201 @@
#include "SqliteDatabase.h"
#include "sqlite3.h"
#include <string.h>
#include <stdio.h>
#include <iostream>
using namespace std;
SqliteDatabase::SqliteDatabase(const char* host) : Database(host,"","")
{
mConnection=NULL;
mCurrentStmt=NULL;
}
void SqliteDatabase::connect()
{
int rc = sqlite3_open(mHost.c_str(), &mConnection);
if( rc )
{
cout << "Can't open database: " << mHost << " " << rc << endl;
sqlite3_close(mConnection);
}
}
void SqliteDatabase::disconnect()
{
sqlite3_finalize(mCurrentStmt);
sqlite3_close(mConnection);
}
// returns true if the query went ok
bool SqliteDatabase::executeSQL(const char* sql, bool fail_ok)
{
sqlite3_finalize(mCurrentStmt);
int rc = sqlite3_prepare_v2(mConnection, sql, -1, &mCurrentStmt, NULL);
if (rc != SQLITE_OK )
{
if (!fail_ok)
{
#ifdef DEBUG
cout << "SQL Perror:" << rc << endl;
cout << "Statement: " << sql << endl;
cout << "Error: " << sqlite3_errmsg(mConnection) << endl;
#endif
}
return false;
}
rc = sqlite3_step(mCurrentStmt);
if (rc == SQLITE_ROW)
{
mMoreRows = true;
}
else if (rc == SQLITE_DONE)
{
mMoreRows = false;
}
else
{
mMoreRows = false;
if (!fail_ok)
{
#ifdef DEBUG
cout << "SQL Serror:" << rc << endl;
cout << "Statement: " << sql << endl;
cout << "Error: " << sqlite3_errmsg(mConnection) << endl;
#endif
}
return false;
}
return true;
}
// tells you how many rows were changed by an update or insert
int SqliteDatabase::getNumRowsAffected()
{
// TODO: SqliteDatabase::getNumRowsAffected()
return(0);
}
int SqliteDatabase::getLastInsertID()
{
return(sqlite3_last_insert_rowid(mConnection));
}
// returns false if there are no results
bool SqliteDatabase::startIterRows()
{
mColNameTable.clear();
mColNameTable.resize(sqlite3_column_count(mCurrentStmt));
for(unsigned n=0; n<mColNameTable.size(); n++)
{
mColNameTable[n]=sqlite3_column_name(mCurrentStmt,n);
}
return(mMoreRows);
}
void SqliteDatabase::endIterRows()
{
sqlite3_finalize(mCurrentStmt);
mCurrentStmt=NULL;
}
// call this after you executeSQL
// will return false if there are no more rows
bool SqliteDatabase::getNextRow()
{
if(!mMoreRows) return(false);
int rc=sqlite3_step(mCurrentStmt);
if(rc==SQLITE_ROW)
{
return(true);
}else if(rc==SQLITE_DONE)
{
return(false);
}else
{
cout << "SQL Rerror:" << rc << endl;
return(false);
}
}
bool SqliteDatabase::getNull(int colIndex)
{
return(SQLITE_NULL == sqlite3_column_type(mCurrentStmt, colIndex));
}
char* SqliteDatabase::getStr(int colIndex,std::string& retStr)
{
retStr=(char*)sqlite3_column_text(mCurrentStmt, colIndex);
return((char*)retStr.c_str());
}
int32 SqliteDatabase::getInt(int colIndex)
{
return(sqlite3_column_int(mCurrentStmt, colIndex));
}
float SqliteDatabase::getFloat(int colIndex)
{
return(sqlite3_column_double(mCurrentStmt, colIndex));
}
bool SqliteDatabase::getBool(int colIndex)
{
return(sqlite3_column_int(mCurrentStmt, colIndex));
}
int SqliteDatabase::getBinary(int colIndex,unsigned char* buf,int maxSize)
{
const void* blob=sqlite3_column_blob(mCurrentStmt, colIndex);
int size=sqlite3_column_bytes(mCurrentStmt, colIndex);
if(size<maxSize) maxSize=size;
memcpy(buf,blob,maxSize);
return(size);
}
std::vector<unsigned char> SqliteDatabase::getBinary(int colIndex)
{
const unsigned char* blob = reinterpret_cast<const unsigned char*>(sqlite3_column_blob(mCurrentStmt, colIndex));
size_t iSize = sqlite3_column_bytes(mCurrentStmt, colIndex);
std::vector<unsigned char> vucResult;
vucResult.resize(iSize);
std::copy(blob, blob+iSize, vucResult.begin());
return vucResult;
}
uint64 SqliteDatabase::getBigInt(int colIndex)
{
return(sqlite3_column_int64(mCurrentStmt, colIndex));
}
/* 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(const unsigned char* start, int size, std::string& retStr)
{
static const char toHex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
retStr.resize(3 + (size * 2));
int pos = 0;
retStr[pos++] = 'X';
retStr[pos++] = '\'';
for (int n = 0; n < size; ++n)
{
retStr[pos++] = toHex[start[n] >> 4];
retStr[pos++] = toHex[start[n] & 0x0f];
}
retStr[pos] = '\'';
}
// vim:ts=4

View File

@@ -0,0 +1,45 @@
#include "database.h"
struct sqlite3;
struct sqlite3_stmt;
class SqliteDatabase : public Database
{
sqlite3* mConnection;
sqlite3_stmt* mCurrentStmt;
bool mMoreRows;
public:
SqliteDatabase(const char* host);
void connect();
void disconnect();
// returns true if the query went ok
bool executeSQL(const char* sql, bool fail_okay);
// tells you how many rows were changed by an update or insert
int getNumRowsAffected();
int getLastInsertID();
// 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();
bool getNull(int colIndex);
char* getStr(int colIndex,std::string& retStr);
int32 getInt(int colIndex);
float getFloat(int colIndex);
bool getBool(int colIndex);
// returns amount stored in buf
int getBinary(int colIndex,unsigned char* buf,int maxSize);
std::vector<unsigned char> getBinary(int colIndex);
uint64 getBigInt(int colIndex);
void escape(const unsigned char* start,int size,std::string& retStr);
};
// vim:ts=4

View File

@@ -0,0 +1,196 @@
#include "database.h"
#include <stdlib.h>
#include <string.h>
Database::Database(const char* host,const char* user,const char* pass) : mNumCol(0)
{
mDBPass = pass;
mHost = host;
mUser = user;
}
Database::~Database()
{
}
bool Database::getNull(const char* colName)
{
int index;
if (getColNumber(colName,&index))
{
return getNull(index);
}
return true;
}
char* Database::getStr(const char* colName,std::string& retStr)
{
int index;
if (getColNumber(colName,&index))
{
return getStr(index,retStr);
}
return NULL;
}
int32 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;
}
int Database::getBinary(const char* colName,unsigned char* buf,int maxSize)
{
int index;
if (getColNumber(colName,&index))
{
return(getBinary(index,buf,maxSize));
}
return(0);
}
std::vector<unsigned char> Database::getBinary(const std::string& strColName)
{
int index;
if (getColNumber(strColName.c_str(), &index))
{
return getBinary(index);
}
return std::vector<unsigned char>();
}
std::string Database::getStrBinary(const std::string& strColName)
{
// YYY Could eliminate a copy if getStrBinary was a template.
return strCopy(getBinary(strColName.c_str()));
}
uint64 Database::getBigInt(const char* colName)
{
int index;
if (getColNumber(colName,&index))
{
return getBigInt(index);
}
return 0;
}
// returns false if can't find col
bool Database::getColNumber(const char* colName,int* retIndex)
{
for (unsigned int n=0; n<mColNameTable.size(); n++)
{
if (strcmp(colName,mColNameTable[n].c_str())==0)
{
*retIndex=n;
return(true);
}
}
return false;
}
#if 0
int Database::getSingleDBValueInt(const char* sql)
{
int ret;
if ( executeSQL(sql) && startIterRows()
{
ret=getInt(0);
endIterRows();
}
else
{
//theUI->statusMsg("ERROR with database: %s",sql);
ret=0;
}
return(ret);
}
#endif
#if 0
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);
}
#endif
#if 0
char* Database::getSingleDBValueStr(const char* sql,std::string& retStr)
{
char* ret;
if (executeSQL(sql) && startIterRows())
{
ret=getStr(0,retStr);
endIterRows();
}
else
{
//theUI->statusMsg("ERROR with database: %s",sql);
ret=0;
}
return(ret);
}
#endif
std::string Database::escape(const std::string strValue)
{
std::string strReturn;
escape(reinterpret_cast<const unsigned char*>(strValue.c_str()), strValue.size(), strReturn);
return strReturn;
}
// vim:ts=4

View File

@@ -0,0 +1,92 @@
#ifndef __DATABASE__
#define __DATABASE__
#include <string>
#include <vector>
#include "../ripple/types.h"
#include "../ripple/utils.h"
#define SQL_FOREACH(_db, _strQuery) \
if ((_db)->executeSQL(_strQuery)) \
for (bool _bMore = (_db)->startIterRows(); _bMore; _bMore = (_db)->getNextRow())
#define SQL_EXISTS(_db, _strQuery) \
((_db)->executeSQL(_strQuery) && (_db)->startIterRows())
/*
this maintains the connection to the database
*/
class Database
{
protected:
int mNumCol;
std::string mUser;
std::string mHost;
std::string mDBPass;
std::vector<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(const unsigned char* start,int size,std::string& retStr)=0;
std::string escape(const std::string strValue);
// returns true if the query went ok
virtual bool executeSQL(const char* sql, bool fail_okay=false)=0;
bool executeSQL(std::string strSql, bool fail_okay=false) {
return executeSQL(strSql.c_str(), fail_okay);
}
// tells you how many rows were changed by an update or insert
virtual int getNumRowsAffected()=0;
virtual int getLastInsertID()=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
bool getNull(const char* colName);
char* getStr(const char* colName,std::string& retStr);
std::string getStrBinary(const std::string& strColName);
int32 getInt(const char* colName);
float getFloat(const char* colName);
bool getBool(const char* colName);
// returns amount stored in buf
int getBinary(const char* colName, unsigned char* buf, int maxSize);
std::vector<unsigned char> getBinary(const std::string& strColName);
uint64 getBigInt(const char* colName);
virtual bool getNull(int colIndex)=0;
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 int getBinary(int colIndex,unsigned char* buf,int maxSize)=0;
virtual uint64 getBigInt(int colIndex)=0;
virtual std::vector<unsigned char> getBinary(int colIndex)=0;
// int getSingleDBValueInt(const char* sql);
// float getSingleDBValueFloat(const char* sql);
// char* getSingleDBValueStr(const char* sql, std::string& retStr);
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,136 @@
#include "mysqldatabase.h"
#include "reportingmechanism.h"
#include "string/i4string.h"
#include <stdlib.h>
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);
}

View File

@@ -0,0 +1,47 @@
#ifndef __MYSQLDATABASE__
#define __MYSQLDATABASE__
#include "../database.h"
#include "string/i4string.h"
#include "mysql/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

134161
src/cpp/database/sqlite3.c Normal file

File diff suppressed because it is too large Load Diff

6999
src/cpp/database/sqlite3.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,447 @@
/*
** 2006 June 7
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the SQLite interface for use by
** shared libraries that want to be imported as extensions into
** an SQLite instance. Shared libraries that intend to be loaded
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
#ifndef _SQLITE3EXT_H_
#define _SQLITE3EXT_H_
#include "sqlite3.h"
typedef struct sqlite3_api_routines sqlite3_api_routines;
/*
** The following structure holds pointers to all of the SQLite API
** routines.
**
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each others' shared
** libraries!
*/
struct sqlite3_api_routines {
void * (*aggregate_context)(sqlite3_context*,int nBytes);
int (*aggregate_count)(sqlite3_context*);
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
int (*bind_double)(sqlite3_stmt*,int,double);
int (*bind_int)(sqlite3_stmt*,int,int);
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
int (*bind_null)(sqlite3_stmt*,int);
int (*bind_parameter_count)(sqlite3_stmt*);
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const char*));
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
int (*column_count)(sqlite3_stmt*pStmt);
const char * (*column_database_name)(sqlite3_stmt*,int);
const void * (*column_database_name16)(sqlite3_stmt*,int);
const char * (*column_decltype)(sqlite3_stmt*,int i);
const void * (*column_decltype16)(sqlite3_stmt*,int);
double (*column_double)(sqlite3_stmt*,int iCol);
int (*column_int)(sqlite3_stmt*,int iCol);
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
const char * (*column_name)(sqlite3_stmt*,int);
const void * (*column_name16)(sqlite3_stmt*,int);
const char * (*column_origin_name)(sqlite3_stmt*,int);
const void * (*column_origin_name16)(sqlite3_stmt*,int);
const char * (*column_table_name)(sqlite3_stmt*,int);
const void * (*column_table_name16)(sqlite3_stmt*,int);
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
const void * (*column_text16)(sqlite3_stmt*,int iCol);
int (*column_type)(sqlite3_stmt*,int iCol);
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
int (*create_collation)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_collation16)(sqlite3*,const void*,int,void*,
int(*)(void*,int,const void*,int,const void*));
int (*create_function)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_function16)(sqlite3*,const void*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
int (*declare_vtab)(sqlite3*,const char*);
int (*enable_shared_cache)(int);
int (*errcode)(sqlite3*db);
const char * (*errmsg)(sqlite3*);
const void * (*errmsg16)(sqlite3*);
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
int (*expired)(sqlite3_stmt*);
int (*finalize)(sqlite3_stmt*pStmt);
void (*free)(void*);
void (*free_table)(char**result);
int (*get_autocommit)(sqlite3*);
void * (*get_auxdata)(sqlite3_context*,int);
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
int (*global_recover)(void);
void (*interruptx)(sqlite3*);
sqlite_int64 (*last_insert_rowid)(sqlite3*);
const char * (*libversion)(void);
int (*libversion_number)(void);
void *(*malloc)(int);
char * (*mprintf)(const char*,...);
int (*open)(const char*,sqlite3**);
int (*open16)(const void*,sqlite3**);
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
void *(*realloc)(void*,int);
int (*reset)(sqlite3_stmt*pStmt);
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_double)(sqlite3_context*,double);
void (*result_error)(sqlite3_context*,const char*,int);
void (*result_error16)(sqlite3_context*,const void*,int);
void (*result_int)(sqlite3_context*,int);
void (*result_int64)(sqlite3_context*,sqlite_int64);
void (*result_null)(sqlite3_context*);
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
int (*value_bytes16)(sqlite3_value*);
double (*value_double)(sqlite3_value*);
int (*value_int)(sqlite3_value*);
sqlite_int64 (*value_int64)(sqlite3_value*);
int (*value_numeric_type)(sqlite3_value*);
const unsigned char * (*value_text)(sqlite3_value*);
const void * (*value_text16)(sqlite3_value*);
const void * (*value_text16be)(sqlite3_value*);
const void * (*value_text16le)(sqlite3_value*);
int (*value_type)(sqlite3_value*);
char *(*vmprintf)(const char*,va_list);
/* Added ??? */
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
/* Added by 3.3.13 */
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
int(*)(void*,int,const void*,int,const void*),
void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
sqlite3_mutex *(*mutex_alloc)(int);
void (*mutex_enter)(sqlite3_mutex*);
void (*mutex_free)(sqlite3_mutex*);
void (*mutex_leave)(sqlite3_mutex*);
int (*mutex_try)(sqlite3_mutex*);
int (*open_v2)(const char*,sqlite3**,int,const char*);
int (*release_memory)(int);
void (*result_error_nomem)(sqlite3_context*);
void (*result_error_toobig)(sqlite3_context*);
int (*sleep)(int);
void (*soft_heap_limit)(int);
sqlite3_vfs *(*vfs_find)(const char*);
int (*vfs_register)(sqlite3_vfs*,int);
int (*vfs_unregister)(sqlite3_vfs*);
int (*xthreadsafe)(void);
void (*result_zeroblob)(sqlite3_context*,int);
void (*result_error_code)(sqlite3_context*,int);
int (*test_control)(int, ...);
void (*randomness)(int,void*);
sqlite3 *(*context_db_handle)(sqlite3_context*);
int (*extended_result_codes)(sqlite3*,int);
int (*limit)(sqlite3*,int,int);
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
const char *(*sql)(sqlite3_stmt*);
int (*status)(int,int*,int*,int);
int (*backup_finish)(sqlite3_backup*);
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
int (*backup_pagecount)(sqlite3_backup*);
int (*backup_remaining)(sqlite3_backup*);
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
int (*extended_errcode)(sqlite3*);
void (*log)(int,const char*,...);
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
const char *(*sourceid)(void);
int (*stmt_status)(sqlite3_stmt*,int,int);
int (*strnicmp)(const char*,const char*,int);
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
int (*wal_autocheckpoint)(sqlite3*,int);
int (*wal_checkpoint)(sqlite3*,const char*);
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
};
/*
** The following macros redefine the API routines so that they are
** redirected throught the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
** it can get access to the sqlite3_api_routines structure
** definition. But the main library does not want to redefine
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#ifndef SQLITE_CORE
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
#endif
#define sqlite3_bind_blob sqlite3_api->bind_blob
#define sqlite3_bind_double sqlite3_api->bind_double
#define sqlite3_bind_int sqlite3_api->bind_int
#define sqlite3_bind_int64 sqlite3_api->bind_int64
#define sqlite3_bind_null sqlite3_api->bind_null
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
#define sqlite3_bind_text sqlite3_api->bind_text
#define sqlite3_bind_text16 sqlite3_api->bind_text16
#define sqlite3_bind_value sqlite3_api->bind_value
#define sqlite3_busy_handler sqlite3_api->busy_handler
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
#define sqlite3_changes sqlite3_api->changes
#define sqlite3_close sqlite3_api->close
#define sqlite3_collation_needed sqlite3_api->collation_needed
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
#define sqlite3_column_blob sqlite3_api->column_blob
#define sqlite3_column_bytes sqlite3_api->column_bytes
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
#define sqlite3_column_count sqlite3_api->column_count
#define sqlite3_column_database_name sqlite3_api->column_database_name
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
#define sqlite3_column_decltype sqlite3_api->column_decltype
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
#define sqlite3_column_double sqlite3_api->column_double
#define sqlite3_column_int sqlite3_api->column_int
#define sqlite3_column_int64 sqlite3_api->column_int64
#define sqlite3_column_name sqlite3_api->column_name
#define sqlite3_column_name16 sqlite3_api->column_name16
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
#define sqlite3_column_table_name sqlite3_api->column_table_name
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
#define sqlite3_column_text sqlite3_api->column_text
#define sqlite3_column_text16 sqlite3_api->column_text16
#define sqlite3_column_type sqlite3_api->column_type
#define sqlite3_column_value sqlite3_api->column_value
#define sqlite3_commit_hook sqlite3_api->commit_hook
#define sqlite3_complete sqlite3_api->complete
#define sqlite3_complete16 sqlite3_api->complete16
#define sqlite3_create_collation sqlite3_api->create_collation
#define sqlite3_create_collation16 sqlite3_api->create_collation16
#define sqlite3_create_function sqlite3_api->create_function
#define sqlite3_create_function16 sqlite3_api->create_function16
#define sqlite3_create_module sqlite3_api->create_module
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
#define sqlite3_data_count sqlite3_api->data_count
#define sqlite3_db_handle sqlite3_api->db_handle
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
#define sqlite3_errcode sqlite3_api->errcode
#define sqlite3_errmsg sqlite3_api->errmsg
#define sqlite3_errmsg16 sqlite3_api->errmsg16
#define sqlite3_exec sqlite3_api->exec
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_expired sqlite3_api->expired
#endif
#define sqlite3_finalize sqlite3_api->finalize
#define sqlite3_free sqlite3_api->free
#define sqlite3_free_table sqlite3_api->free_table
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
#define sqlite3_get_table sqlite3_api->get_table
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_global_recover sqlite3_api->global_recover
#endif
#define sqlite3_interrupt sqlite3_api->interruptx
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
#define sqlite3_libversion sqlite3_api->libversion
#define sqlite3_libversion_number sqlite3_api->libversion_number
#define sqlite3_malloc sqlite3_api->malloc
#define sqlite3_mprintf sqlite3_api->mprintf
#define sqlite3_open sqlite3_api->open
#define sqlite3_open16 sqlite3_api->open16
#define sqlite3_prepare sqlite3_api->prepare
#define sqlite3_prepare16 sqlite3_api->prepare16
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_profile sqlite3_api->profile
#define sqlite3_progress_handler sqlite3_api->progress_handler
#define sqlite3_realloc sqlite3_api->realloc
#define sqlite3_reset sqlite3_api->reset
#define sqlite3_result_blob sqlite3_api->result_blob
#define sqlite3_result_double sqlite3_api->result_double
#define sqlite3_result_error sqlite3_api->result_error
#define sqlite3_result_error16 sqlite3_api->result_error16
#define sqlite3_result_int sqlite3_api->result_int
#define sqlite3_result_int64 sqlite3_api->result_int64
#define sqlite3_result_null sqlite3_api->result_null
#define sqlite3_result_text sqlite3_api->result_text
#define sqlite3_result_text16 sqlite3_api->result_text16
#define sqlite3_result_text16be sqlite3_api->result_text16be
#define sqlite3_result_text16le sqlite3_api->result_text16le
#define sqlite3_result_value sqlite3_api->result_value
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
#define sqlite3_snprintf sqlite3_api->snprintf
#define sqlite3_step sqlite3_api->step
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
#define sqlite3_total_changes sqlite3_api->total_changes
#define sqlite3_trace sqlite3_api->trace
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
#endif
#define sqlite3_update_hook sqlite3_api->update_hook
#define sqlite3_user_data sqlite3_api->user_data
#define sqlite3_value_blob sqlite3_api->value_blob
#define sqlite3_value_bytes sqlite3_api->value_bytes
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
#define sqlite3_value_double sqlite3_api->value_double
#define sqlite3_value_int sqlite3_api->value_int
#define sqlite3_value_int64 sqlite3_api->value_int64
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
#define sqlite3_value_text sqlite3_api->value_text
#define sqlite3_value_text16 sqlite3_api->value_text16
#define sqlite3_value_text16be sqlite3_api->value_text16be
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
#define sqlite3_blob_close sqlite3_api->blob_close
#define sqlite3_blob_open sqlite3_api->blob_open
#define sqlite3_blob_read sqlite3_api->blob_read
#define sqlite3_blob_write sqlite3_api->blob_write
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
#define sqlite3_file_control sqlite3_api->file_control
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
#define sqlite3_memory_used sqlite3_api->memory_used
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
#define sqlite3_mutex_free sqlite3_api->mutex_free
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
#define sqlite3_mutex_try sqlite3_api->mutex_try
#define sqlite3_open_v2 sqlite3_api->open_v2
#define sqlite3_release_memory sqlite3_api->release_memory
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
#define sqlite3_sleep sqlite3_api->sleep
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
#define sqlite3_vfs_find sqlite3_api->vfs_find
#define sqlite3_vfs_register sqlite3_api->vfs_register
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
#define sqlite3_result_error_code sqlite3_api->result_error_code
#define sqlite3_test_control sqlite3_api->test_control
#define sqlite3_randomness sqlite3_api->randomness
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
#define sqlite3_limit sqlite3_api->limit
#define sqlite3_next_stmt sqlite3_api->next_stmt
#define sqlite3_sql sqlite3_api->sql
#define sqlite3_status sqlite3_api->status
#define sqlite3_backup_finish sqlite3_api->backup_finish
#define sqlite3_backup_init sqlite3_api->backup_init
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
#define sqlite3_backup_step sqlite3_api->backup_step
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
#define sqlite3_db_config sqlite3_api->db_config
#define sqlite3_db_mutex sqlite3_api->db_mutex
#define sqlite3_db_status sqlite3_api->db_status
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
#define sqlite3_log sqlite3_api->log
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
#define sqlite3_sourceid sqlite3_api->sourceid
#define sqlite3_stmt_status sqlite3_api->stmt_status
#define sqlite3_strnicmp sqlite3_api->strnicmp
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
#define sqlite3_wal_hook sqlite3_api->wal_hook
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
#endif /* SQLITE_CORE */
#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
#endif /* _SQLITE3EXT_H_ */

View File

@@ -0,0 +1,82 @@
#ifndef __TMYODBC_UTILITY_H__
#define __TMYODBC_UTILITY_H__
#ifdef HAVE_CONFIG_H
#include <myconf.h>
#endif
/* STANDARD C HEADERS */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/* ODBC HEADERS */
#include <sqlext.h>
#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__ */

View File

@@ -0,0 +1,246 @@
#include "windatabase.h"
#include "dbutility.h"
using namespace std;
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.c_str(), SQL_NTS, (unsigned char*)(char*) mUser.c_str(), SQL_NTS, (unsigned char*)(char*) mDBPass.c_str(), 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, bool fail_okay)
{
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)
{
mColNameTable.resize(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,string& 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((char*)retStr.c_str());
}
int32 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);
}
// TODO
void WinDatabase::escape(const unsigned char* start,int size,std::string& retStr)
{
retStr=(char*)start;
}
// TODO
int WinDatabase::getLastInsertID()
{
return(0);
}
uint64 WinDatabase::getBigInt(int colIndex)
{
colIndex++;
uint64 ret=0;
SQLRETURN rc = SQLGetData(hstmt,colIndex,SQL_INTEGER,&ret,sizeof(uint64),NULL);
mystmt(hstmt,rc);
return(ret);
}
// TODO:
int WinDatabase::getBinary(int colIndex,unsigned char* buf,int maxSize)
{
return(0);
}
std::vector<unsigned char> WinDatabase::getBinary(int colIndex)
{
// TODO:
std::vector<unsigned char> vucResult;
return vucResult;
}

View File

@@ -0,0 +1,61 @@
#ifndef __WINDATABASE__
#define __WINDATABASE__
#include "../database.h"
#ifdef WIN32
#define _WINSOCKAPI_ // prevent inclusion of winsock.h in windows.h
#include <windows.h>
#include "sql.h"
#endif
/*
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((char*)mDBPass.c_str()); }
// returns true if the query went ok
bool executeSQL(const char* sql, bool fail_okay=false);
int getNumRowsAffected();
int getLastInsertID();
// 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,std::string& retStr);
int32 getInt(int colIndex);
float getFloat(int colIndex);
bool getBool(int colIndex);
uint64 getBigInt(int colIndex);
int getBinary(int colIndex,unsigned char* buf,int maxSize);
std::vector<unsigned char> getBinary(int colIndex);
bool getNull(int colIndex){ return(true); }
void escape(const unsigned char* start,int size,std::string& retStr);
};
#endif

BIN
src/cpp/json/LICENSE Normal file

Binary file not shown.

19
src/cpp/json/autolink.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef JSON_AUTOLINK_H_INCLUDED
# define JSON_AUTOLINK_H_INCLUDED
# include "config.h"
# ifdef JSON_IN_CPPTL
# include <cpptl/cpptl_autolink.h>
# endif
# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL)
# define CPPTL_AUTOLINK_NAME "json"
# undef CPPTL_AUTOLINK_DLL
# ifdef JSON_DLL
# define CPPTL_AUTOLINK_DLL
# endif
# include "autolink.h"
# endif
#endif // JSON_AUTOLINK_H_INCLUDED

43
src/cpp/json/config.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef JSON_CONFIG_H_INCLUDED
# define JSON_CONFIG_H_INCLUDED
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
/// If defined, indicates that Json specific container should be used
/// (hash table & simple deque container with customizable allocator).
/// THIS FEATURE IS STILL EXPERIMENTAL!
//# define JSON_VALUE_USE_INTERNAL_MAP 1
/// Force usage of standard new/malloc based allocator instead of memory pool based allocator.
/// The memory pools allocator used optimization (initializing Value and ValueInternalLink
/// as if it was a POD) that may cause some validation tool to report errors.
/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined.
//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1
/// If defined, indicates that Json use exception to report invalid type manipulation
/// instead of C assert macro.
# define JSON_USE_EXCEPTION 1
# ifdef JSON_IN_CPPTL
# include <cpptl/config.h>
# ifndef JSON_USE_CPPTL
# define JSON_USE_CPPTL 1
# endif
# endif
# ifdef JSON_IN_CPPTL
# define JSON_API CPPTL_API
# elif defined(JSON_DLL_BUILD)
# define JSON_API __declspec(dllexport)
# elif defined(JSON_DLL)
# define JSON_API __declspec(dllimport)
# else
# define JSON_API
# endif
#endif // JSON_CONFIG_H_INCLUDED

42
src/cpp/json/features.h Normal file
View File

@@ -0,0 +1,42 @@
#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
# define CPPTL_JSON_FEATURES_H_INCLUDED
# include "forwards.h"
namespace Json {
/** \brief Configuration passed to reader and writer.
* This configuration object can be used to force the Reader or Writer
* to behave in a standard conforming way.
*/
class JSON_API Features
{
public:
/** \brief A configuration that allows all features and assumes all strings are UTF-8.
* - C & C++ comments are allowed
* - Root object can be any JSON value
* - Assumes Value strings are encoded in UTF-8
*/
static Features all();
/** \brief A configuration that is strictly compatible with the JSON specification.
* - Comments are forbidden.
* - Root object must be either an array or an object value.
* - Assumes Value strings are encoded in UTF-8
*/
static Features strictMode();
/** \brief Initialize the configuration like JsonConfig::allFeatures;
*/
Features();
/// \c true if comments are allowed. Default: \c true.
bool allowComments_;
/// \c true if root must be either an array or an object value. Default: \c false.
bool strictRoot_;
};
} // namespace Json
#endif // CPPTL_JSON_FEATURES_H_INCLUDED

39
src/cpp/json/forwards.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef JSON_FORWARDS_H_INCLUDED
# define JSON_FORWARDS_H_INCLUDED
# include "config.h"
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef int Int;
typedef unsigned int UInt;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
#ifdef JSON_VALUE_USE_INTERNAL_MAP
class ValueAllocator;
class ValueMapAllocator;
class ValueInternalLink;
class ValueInternalArray;
class ValueInternalMap;
#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED

10
src/cpp/json/json.h Normal file
View File

@@ -0,0 +1,10 @@
#ifndef JSON_JSON_H_INCLUDED
# define JSON_JSON_H_INCLUDED
# include "autolink.h"
# include "value.h"
# include "reader.h"
# include "writer.h"
# include "features.h"
#endif // JSON_JSON_H_INCLUDED

View File

@@ -0,0 +1,125 @@
#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED
# define JSONCPP_BATCHALLOCATOR_H_INCLUDED
# include <stdlib.h>
# include <assert.h>
# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
namespace Json {
/* Fast memory allocator.
*
* This memory allocator allocates memory for a batch of object (specified by
* the page size, the number of object in each page).
*
* It does not allow the destruction of a single object. All the allocated objects
* can be destroyed at once. The memory can be either released or reused for future
* allocation.
*
* The in-place new operator must be used to construct the object using the pointer
* returned by allocate.
*/
template<typename AllocatedType
,const unsigned int objectPerAllocation>
class BatchAllocator
{
public:
typedef AllocatedType Type;
BatchAllocator( unsigned int objectsPerPage = 255 )
: freeHead_( 0 )
, objectsPerPage_( objectsPerPage )
{
// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() );
assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space.
assert( objectsPerPage >= 16 );
batches_ = allocateBatch( 0 ); // allocated a dummy page
currentBatch_ = batches_;
}
~BatchAllocator()
{
for ( BatchInfo *batch = batches_; batch; )
{
BatchInfo *nextBatch = batch->next_;
free( batch );
batch = nextBatch;
}
}
/// allocate space for an array of objectPerAllocation object.
/// @warning it is the responsability of the caller to call objects constructors.
AllocatedType *allocate()
{
if ( freeHead_ ) // returns node from free list.
{
AllocatedType *object = freeHead_;
freeHead_ = *(AllocatedType **)object;
return object;
}
if ( currentBatch_->used_ == currentBatch_->end_ )
{
currentBatch_ = currentBatch_->next_;
while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ )
currentBatch_ = currentBatch_->next_;
if ( !currentBatch_ ) // no free batch found, allocate a new one
{
currentBatch_ = allocateBatch( objectsPerPage_ );
currentBatch_->next_ = batches_; // insert at the head of the list
batches_ = currentBatch_;
}
}
AllocatedType *allocated = currentBatch_->used_;
currentBatch_->used_ += objectPerAllocation;
return allocated;
}
/// Release the object.
/// @warning it is the responsability of the caller to actually destruct the object.
void release( AllocatedType *object )
{
assert( object != 0 );
*(AllocatedType **)object = freeHead_;
freeHead_ = object;
}
private:
struct BatchInfo
{
BatchInfo *next_;
AllocatedType *used_;
AllocatedType *end_;
AllocatedType buffer_[objectPerAllocation];
};
// disabled copy constructor and assignement operator.
BatchAllocator( const BatchAllocator & );
void operator =( const BatchAllocator &);
static BatchInfo *allocateBatch( unsigned int objectsPerPage )
{
const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation
+ sizeof(AllocatedType) * objectPerAllocation * objectsPerPage;
BatchInfo *batch = static_cast<BatchInfo*>( malloc( mallocSize ) );
batch->next_ = 0;
batch->used_ = batch->buffer_;
batch->end_ = batch->buffer_ + objectsPerPage;
return batch;
}
BatchInfo *batches_;
BatchInfo *currentBatch_;
/// Head of a single linked list within the allocated space of freeed object
AllocatedType *freeHead_;
unsigned int objectsPerPage_;
};
} // namespace Json
# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION
#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED

View File

@@ -0,0 +1,448 @@
// included by json_value.cpp
// everything is within Json namespace
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueInternalArray
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueArrayAllocator::~ValueArrayAllocator()
{
}
// //////////////////////////////////////////////////////////////////
// class DefaultValueArrayAllocator
// //////////////////////////////////////////////////////////////////
#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
class DefaultValueArrayAllocator : public ValueArrayAllocator
{
public: // overridden from ValueArrayAllocator
virtual ~DefaultValueArrayAllocator()
{
}
virtual ValueInternalArray *newArray()
{
return new ValueInternalArray();
}
virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
{
return new ValueInternalArray( other );
}
virtual void destructArray( ValueInternalArray *array )
{
delete array;
}
virtual void reallocateArrayPageIndex( Value **&indexes,
ValueInternalArray::PageIndex &indexCount,
ValueInternalArray::PageIndex minNewIndexCount )
{
ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
if ( minNewIndexCount > newIndexCount )
newIndexCount = minNewIndexCount;
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
if ( !newIndexes )
throw std::bad_alloc();
indexCount = newIndexCount;
indexes = static_cast<Value **>( newIndexes );
}
virtual void releaseArrayPageIndex( Value **indexes,
ValueInternalArray::PageIndex indexCount )
{
if ( indexes )
free( indexes );
}
virtual Value *allocateArrayPage()
{
return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) );
}
virtual void releaseArrayPage( Value *value )
{
if ( value )
free( value );
}
};
#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
/// @todo make this thread-safe (lock when accessign batch allocator)
class DefaultValueArrayAllocator : public ValueArrayAllocator
{
public: // overridden from ValueArrayAllocator
virtual ~DefaultValueArrayAllocator()
{
}
virtual ValueInternalArray *newArray()
{
ValueInternalArray *array = arraysAllocator_.allocate();
new (array) ValueInternalArray(); // placement new
return array;
}
virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
{
ValueInternalArray *array = arraysAllocator_.allocate();
new (array) ValueInternalArray( other ); // placement new
return array;
}
virtual void destructArray( ValueInternalArray *array )
{
if ( array )
{
array->~ValueInternalArray();
arraysAllocator_.release( array );
}
}
virtual void reallocateArrayPageIndex( Value **&indexes,
ValueInternalArray::PageIndex &indexCount,
ValueInternalArray::PageIndex minNewIndexCount )
{
ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
if ( minNewIndexCount > newIndexCount )
newIndexCount = minNewIndexCount;
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
if ( !newIndexes )
throw std::bad_alloc();
indexCount = newIndexCount;
indexes = static_cast<Value **>( newIndexes );
}
virtual void releaseArrayPageIndex( Value **indexes,
ValueInternalArray::PageIndex indexCount )
{
if ( indexes )
free( indexes );
}
virtual Value *allocateArrayPage()
{
return static_cast<Value *>( pagesAllocator_.allocate() );
}
virtual void releaseArrayPage( Value *value )
{
if ( value )
pagesAllocator_.release( value );
}
private:
BatchAllocator<ValueInternalArray,1> arraysAllocator_;
BatchAllocator<Value,ValueInternalArray::itemsPerPage> pagesAllocator_;
};
#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
static ValueArrayAllocator *&arrayAllocator()
{
static DefaultValueArrayAllocator defaultAllocator;
static ValueArrayAllocator *arrayAllocator = &defaultAllocator;
return arrayAllocator;
}
static struct DummyArrayAllocatorInitializer {
DummyArrayAllocatorInitializer()
{
arrayAllocator(); // ensure arrayAllocator() statics are initialized before main().
}
} dummyArrayAllocatorInitializer;
// //////////////////////////////////////////////////////////////////
// class ValueInternalArray
// //////////////////////////////////////////////////////////////////
bool
ValueInternalArray::equals( const IteratorState &x,
const IteratorState &other )
{
return x.array_ == other.array_
&& x.currentItemIndex_ == other.currentItemIndex_
&& x.currentPageIndex_ == other.currentPageIndex_;
}
void
ValueInternalArray::increment( IteratorState &it )
{
JSON_ASSERT_MESSAGE( it.array_ &&
(it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_
!= it.array_->size_,
"ValueInternalArray::increment(): moving iterator beyond end" );
++(it.currentItemIndex_);
if ( it.currentItemIndex_ == itemsPerPage )
{
it.currentItemIndex_ = 0;
++(it.currentPageIndex_);
}
}
void
ValueInternalArray::decrement( IteratorState &it )
{
JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_
&& it.currentItemIndex_ == 0,
"ValueInternalArray::decrement(): moving iterator beyond end" );
if ( it.currentItemIndex_ == 0 )
{
it.currentItemIndex_ = itemsPerPage-1;
--(it.currentPageIndex_);
}
else
{
--(it.currentItemIndex_);
}
}
Value &
ValueInternalArray::unsafeDereference( const IteratorState &it )
{
return (*(it.currentPageIndex_))[it.currentItemIndex_];
}
Value &
ValueInternalArray::dereference( const IteratorState &it )
{
JSON_ASSERT_MESSAGE( it.array_ &&
(it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_
< it.array_->size_,
"ValueInternalArray::dereference(): dereferencing invalid iterator" );
return unsafeDereference( it );
}
void
ValueInternalArray::makeBeginIterator( IteratorState &it ) const
{
it.array_ = const_cast<ValueInternalArray *>( this );
it.currentItemIndex_ = 0;
it.currentPageIndex_ = pages_;
}
void
ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const
{
it.array_ = const_cast<ValueInternalArray *>( this );
it.currentItemIndex_ = index % itemsPerPage;
it.currentPageIndex_ = pages_ + index / itemsPerPage;
}
void
ValueInternalArray::makeEndIterator( IteratorState &it ) const
{
makeIterator( it, size_ );
}
ValueInternalArray::ValueInternalArray()
: pages_( 0 )
, size_( 0 )
, pageCount_( 0 )
{
}
ValueInternalArray::ValueInternalArray( const ValueInternalArray &other )
: pages_( 0 )
, pageCount_( 0 )
, size_( other.size_ )
{
PageIndex minNewPages = other.size_ / itemsPerPage;
arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages,
"ValueInternalArray::reserve(): bad reallocation" );
IteratorState itOther;
other.makeBeginIterator( itOther );
Value *value;
for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) )
{
if ( index % itemsPerPage == 0 )
{
PageIndex pageIndex = index / itemsPerPage;
value = arrayAllocator()->allocateArrayPage();
pages_[pageIndex] = value;
}
new (value) Value( dereference( itOther ) );
}
}
ValueInternalArray &
ValueInternalArray::operator =( const ValueInternalArray &other )
{
ValueInternalArray temp( other );
swap( temp );
return *this;
}
ValueInternalArray::~ValueInternalArray()
{
// destroy all constructed items
IteratorState it;
IteratorState itEnd;
makeBeginIterator( it);
makeEndIterator( itEnd );
for ( ; !equals(it,itEnd); increment(it) )
{
Value *value = &dereference(it);
value->~Value();
}
// release all pages
PageIndex lastPageIndex = size_ / itemsPerPage;
for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex )
arrayAllocator()->releaseArrayPage( pages_[pageIndex] );
// release pages index
arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ );
}
void
ValueInternalArray::swap( ValueInternalArray &other )
{
Value **tempPages = pages_;
pages_ = other.pages_;
other.pages_ = tempPages;
ArrayIndex tempSize = size_;
size_ = other.size_;
other.size_ = tempSize;
PageIndex tempPageCount = pageCount_;
pageCount_ = other.pageCount_;
other.pageCount_ = tempPageCount;
}
void
ValueInternalArray::clear()
{
ValueInternalArray dummy;
swap( dummy );
}
void
ValueInternalArray::resize( ArrayIndex newSize )
{
if ( newSize == 0 )
clear();
else if ( newSize < size_ )
{
IteratorState it;
IteratorState itEnd;
makeIterator( it, newSize );
makeIterator( itEnd, size_ );
for ( ; !equals(it,itEnd); increment(it) )
{
Value *value = &dereference(it);
value->~Value();
}
PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage;
PageIndex lastPageIndex = size_ / itemsPerPage;
for ( ; pageIndex < lastPageIndex; ++pageIndex )
arrayAllocator()->releaseArrayPage( pages_[pageIndex] );
size_ = newSize;
}
else if ( newSize > size_ )
resolveReference( newSize );
}
void
ValueInternalArray::makeIndexValid( ArrayIndex index )
{
// Need to enlarge page index ?
if ( index >= pageCount_ * itemsPerPage )
{
PageIndex minNewPages = (index + 1) / itemsPerPage;
arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" );
}
// Need to allocate new pages ?
ArrayIndex nextPageIndex =
(size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage
: size_;
if ( nextPageIndex <= index )
{
PageIndex pageIndex = nextPageIndex / itemsPerPage;
PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1;
for ( ; pageToAllocate-- > 0; ++pageIndex )
pages_[pageIndex] = arrayAllocator()->allocateArrayPage();
}
// Initialize all new entries
IteratorState it;
IteratorState itEnd;
makeIterator( it, size_ );
size_ = index + 1;
makeIterator( itEnd, size_ );
for ( ; !equals(it,itEnd); increment(it) )
{
Value *value = &dereference(it);
new (value) Value(); // Construct a default value using placement new
}
}
Value &
ValueInternalArray::resolveReference( ArrayIndex index )
{
if ( index >= size_ )
makeIndexValid( index );
return pages_[index/itemsPerPage][index%itemsPerPage];
}
Value *
ValueInternalArray::find( ArrayIndex index ) const
{
if ( index >= size_ )
return 0;
return &(pages_[index/itemsPerPage][index%itemsPerPage]);
}
ValueInternalArray::ArrayIndex
ValueInternalArray::size() const
{
return size_;
}
int
ValueInternalArray::distance( const IteratorState &x, const IteratorState &y )
{
return indexOf(y) - indexOf(x);
}
ValueInternalArray::ArrayIndex
ValueInternalArray::indexOf( const IteratorState &iterator )
{
if ( !iterator.array_ )
return ArrayIndex(-1);
return ArrayIndex(
(iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage
+ iterator.currentItemIndex_ );
}
int
ValueInternalArray::compare( const ValueInternalArray &other ) const
{
int sizeDiff( size_ - other.size_ );
if ( sizeDiff != 0 )
return sizeDiff;
for ( ArrayIndex index =0; index < size_; ++index )
{
int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare(
other.pages_[index/itemsPerPage][index%itemsPerPage] );
if ( diff != 0 )
return diff;
}
return 0;
}

View File

@@ -0,0 +1,607 @@
// included by json_value.cpp
// everything is within Json namespace
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueInternalMap
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) );
* This optimization is used by the fast allocator.
*/
ValueInternalLink::ValueInternalLink()
: previous_( 0 )
, next_( 0 )
{
}
ValueInternalLink::~ValueInternalLink()
{
for ( int index =0; index < itemPerLink; ++index )
{
if ( !items_[index].isItemAvailable() )
{
if ( !items_[index].isMemberNameStatic() )
free( keys_[index] );
}
else
break;
}
}
ValueMapAllocator::~ValueMapAllocator()
{
}
#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
class DefaultValueMapAllocator : public ValueMapAllocator
{
public: // overridden from ValueMapAllocator
virtual ValueInternalMap *newMap()
{
return new ValueInternalMap();
}
virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
{
return new ValueInternalMap( other );
}
virtual void destructMap( ValueInternalMap *map )
{
delete map;
}
virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
{
return new ValueInternalLink[size];
}
virtual void releaseMapBuckets( ValueInternalLink *links )
{
delete [] links;
}
virtual ValueInternalLink *allocateMapLink()
{
return new ValueInternalLink();
}
virtual void releaseMapLink( ValueInternalLink *link )
{
delete link;
}
};
#else
/// @todo make this thread-safe (lock when accessign batch allocator)
class DefaultValueMapAllocator : public ValueMapAllocator
{
public: // overridden from ValueMapAllocator
virtual ValueInternalMap *newMap()
{
ValueInternalMap *map = mapsAllocator_.allocate();
new (map) ValueInternalMap(); // placement new
return map;
}
virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
{
ValueInternalMap *map = mapsAllocator_.allocate();
new (map) ValueInternalMap( other ); // placement new
return map;
}
virtual void destructMap( ValueInternalMap *map )
{
if ( map )
{
map->~ValueInternalMap();
mapsAllocator_.release( map );
}
}
virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
{
return new ValueInternalLink[size];
}
virtual void releaseMapBuckets( ValueInternalLink *links )
{
delete [] links;
}
virtual ValueInternalLink *allocateMapLink()
{
ValueInternalLink *link = linksAllocator_.allocate();
memset( link, 0, sizeof(ValueInternalLink) );
return link;
}
virtual void releaseMapLink( ValueInternalLink *link )
{
link->~ValueInternalLink();
linksAllocator_.release( link );
}
private:
BatchAllocator<ValueInternalMap,1> mapsAllocator_;
BatchAllocator<ValueInternalLink,1> linksAllocator_;
};
#endif
static ValueMapAllocator *&mapAllocator()
{
static DefaultValueMapAllocator defaultAllocator;
static ValueMapAllocator *mapAllocator = &defaultAllocator;
return mapAllocator;
}
static struct DummyMapAllocatorInitializer {
DummyMapAllocatorInitializer()
{
mapAllocator(); // ensure mapAllocator() statics are initialized before main().
}
} dummyMapAllocatorInitializer;
// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32.
/*
use linked list hash map.
buckets array is a container.
linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124)
value have extra state: valid, available, deleted
*/
ValueInternalMap::ValueInternalMap()
: buckets_( 0 )
, tailLink_( 0 )
, bucketsSize_( 0 )
, itemCount_( 0 )
{
}
ValueInternalMap::ValueInternalMap( const ValueInternalMap &other )
: buckets_( 0 )
, tailLink_( 0 )
, bucketsSize_( 0 )
, itemCount_( 0 )
{
reserve( other.itemCount_ );
IteratorState it;
IteratorState itEnd;
other.makeBeginIterator( it );
other.makeEndIterator( itEnd );
for ( ; !equals(it,itEnd); increment(it) )
{
bool isStatic;
const char *memberName = key( it, isStatic );
const Value &aValue = value( it );
resolveReference(memberName, isStatic) = aValue;
}
}
ValueInternalMap &
ValueInternalMap::operator =( const ValueInternalMap &other )
{
ValueInternalMap dummy( other );
swap( dummy );
return *this;
}
ValueInternalMap::~ValueInternalMap()
{
if ( buckets_ )
{
for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex )
{
ValueInternalLink *link = buckets_[bucketIndex].next_;
while ( link )
{
ValueInternalLink *linkToRelease = link;
link = link->next_;
mapAllocator()->releaseMapLink( linkToRelease );
}
}
mapAllocator()->releaseMapBuckets( buckets_ );
}
}
void
ValueInternalMap::swap( ValueInternalMap &other )
{
ValueInternalLink *tempBuckets = buckets_;
buckets_ = other.buckets_;
other.buckets_ = tempBuckets;
ValueInternalLink *tempTailLink = tailLink_;
tailLink_ = other.tailLink_;
other.tailLink_ = tempTailLink;
BucketIndex tempBucketsSize = bucketsSize_;
bucketsSize_ = other.bucketsSize_;
other.bucketsSize_ = tempBucketsSize;
BucketIndex tempItemCount = itemCount_;
itemCount_ = other.itemCount_;
other.itemCount_ = tempItemCount;
}
void
ValueInternalMap::clear()
{
ValueInternalMap dummy;
swap( dummy );
}
ValueInternalMap::BucketIndex
ValueInternalMap::size() const
{
return itemCount_;
}
bool
ValueInternalMap::reserveDelta( BucketIndex growth )
{
return reserve( itemCount_ + growth );
}
bool
ValueInternalMap::reserve( BucketIndex newItemCount )
{
if ( !buckets_ && newItemCount > 0 )
{
buckets_ = mapAllocator()->allocateMapBuckets( 1 );
bucketsSize_ = 1;
tailLink_ = &buckets_[0];
}
// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink;
return true;
}
const Value *
ValueInternalMap::find( const char *key ) const
{
if ( !bucketsSize_ )
return 0;
HashKey hashedKey = hash( key );
BucketIndex bucketIndex = hashedKey % bucketsSize_;
for ( const ValueInternalLink *current = &buckets_[bucketIndex];
current != 0;
current = current->next_ )
{
for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index )
{
if ( current->items_[index].isItemAvailable() )
return 0;
if ( strcmp( key, current->keys_[index] ) == 0 )
return &current->items_[index];
}
}
return 0;
}
Value *
ValueInternalMap::find( const char *key )
{
const ValueInternalMap *constThis = this;
return const_cast<Value *>( constThis->find( key ) );
}
Value &
ValueInternalMap::resolveReference( const char *key,
bool isStatic )
{
HashKey hashedKey = hash( key );
if ( bucketsSize_ )
{
BucketIndex bucketIndex = hashedKey % bucketsSize_;
ValueInternalLink **previous = 0;
BucketIndex index;
for ( ValueInternalLink *current = &buckets_[bucketIndex];
current != 0;
previous = &current->next_, current = current->next_ )
{
for ( index=0; index < ValueInternalLink::itemPerLink; ++index )
{
if ( current->items_[index].isItemAvailable() )
return setNewItem( key, isStatic, current, index );
if ( strcmp( key, current->keys_[index] ) == 0 )
return current->items_[index];
}
}
}
reserveDelta( 1 );
return unsafeAdd( key, isStatic, hashedKey );
}
void
ValueInternalMap::remove( const char *key )
{
HashKey hashedKey = hash( key );
if ( !bucketsSize_ )
return;
BucketIndex bucketIndex = hashedKey % bucketsSize_;
for ( ValueInternalLink *link = &buckets_[bucketIndex];
link != 0;
link = link->next_ )
{
BucketIndex index;
for ( index =0; index < ValueInternalLink::itemPerLink; ++index )
{
if ( link->items_[index].isItemAvailable() )
return;
if ( strcmp( key, link->keys_[index] ) == 0 )
{
doActualRemove( link, index, bucketIndex );
return;
}
}
}
}
void
ValueInternalMap::doActualRemove( ValueInternalLink *link,
BucketIndex index,
BucketIndex bucketIndex )
{
// find last item of the bucket and swap it with the 'removed' one.
// set removed items flags to 'available'.
// if last page only contains 'available' items, then desallocate it (it's empty)
ValueInternalLink *&lastLink = getLastLinkInBucket( index );
BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1
for ( ;
lastItemIndex < ValueInternalLink::itemPerLink;
++lastItemIndex ) // may be optimized with dicotomic search
{
if ( lastLink->items_[lastItemIndex].isItemAvailable() )
break;
}
BucketIndex lastUsedIndex = lastItemIndex - 1;
Value *valueToDelete = &link->items_[index];
Value *valueToPreserve = &lastLink->items_[lastUsedIndex];
if ( valueToDelete != valueToPreserve )
valueToDelete->swap( *valueToPreserve );
if ( lastUsedIndex == 0 ) // page is now empty
{ // remove it from bucket linked list and delete it.
ValueInternalLink *linkPreviousToLast = lastLink->previous_;
if ( linkPreviousToLast != 0 ) // can not deleted bucket link.
{
mapAllocator()->releaseMapLink( lastLink );
linkPreviousToLast->next_ = 0;
lastLink = linkPreviousToLast;
}
}
else
{
Value dummy;
valueToPreserve->swap( dummy ); // restore deleted to default Value.
valueToPreserve->setItemUsed( false );
}
--itemCount_;
}
ValueInternalLink *&
ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex )
{
if ( bucketIndex == bucketsSize_ - 1 )
return tailLink_;
ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_;
if ( !previous )
previous = &buckets_[bucketIndex];
return previous;
}
Value &
ValueInternalMap::setNewItem( const char *key,
bool isStatic,
ValueInternalLink *link,
BucketIndex index )
{
char *duplicatedKey = valueAllocator()->makeMemberName( key );
++itemCount_;
link->keys_[index] = duplicatedKey;
link->items_[index].setItemUsed();
link->items_[index].setMemberNameIsStatic( isStatic );
return link->items_[index]; // items already default constructed.
}
Value &
ValueInternalMap::unsafeAdd( const char *key,
bool isStatic,
HashKey hashedKey )
{
JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." );
BucketIndex bucketIndex = hashedKey % bucketsSize_;
ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex );
ValueInternalLink *link = previousLink;
BucketIndex index;
for ( index =0; index < ValueInternalLink::itemPerLink; ++index )
{
if ( link->items_[index].isItemAvailable() )
break;
}
if ( index == ValueInternalLink::itemPerLink ) // need to add a new page
{
ValueInternalLink *newLink = mapAllocator()->allocateMapLink();
index = 0;
link->next_ = newLink;
previousLink = newLink;
link = newLink;
}
return setNewItem( key, isStatic, link, index );
}
ValueInternalMap::HashKey
ValueInternalMap::hash( const char *key ) const
{
HashKey hash = 0;
while ( *key )
hash += *key++ * 37;
return hash;
}
int
ValueInternalMap::compare( const ValueInternalMap &other ) const
{
int sizeDiff( itemCount_ - other.itemCount_ );
if ( sizeDiff != 0 )
return sizeDiff;
// Strict order guaranty is required. Compare all keys FIRST, then compare values.
IteratorState it;
IteratorState itEnd;
makeBeginIterator( it );
makeEndIterator( itEnd );
for ( ; !equals(it,itEnd); increment(it) )
{
if ( !other.find( key( it ) ) )
return 1;
}
// All keys are equals, let's compare values
makeBeginIterator( it );
for ( ; !equals(it,itEnd); increment(it) )
{
const Value *otherValue = other.find( key( it ) );
int valueDiff = value(it).compare( *otherValue );
if ( valueDiff != 0 )
return valueDiff;
}
return 0;
}
void
ValueInternalMap::makeBeginIterator( IteratorState &it ) const
{
it.map_ = const_cast<ValueInternalMap *>( this );
it.bucketIndex_ = 0;
it.itemIndex_ = 0;
it.link_ = buckets_;
}
void
ValueInternalMap::makeEndIterator( IteratorState &it ) const
{
it.map_ = const_cast<ValueInternalMap *>( this );
it.bucketIndex_ = bucketsSize_;
it.itemIndex_ = 0;
it.link_ = 0;
}
bool
ValueInternalMap::equals( const IteratorState &x, const IteratorState &other )
{
return x.map_ == other.map_
&& x.bucketIndex_ == other.bucketIndex_
&& x.link_ == other.link_
&& x.itemIndex_ == other.itemIndex_;
}
void
ValueInternalMap::incrementBucket( IteratorState &iterator )
{
++iterator.bucketIndex_;
JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_,
"ValueInternalMap::increment(): attempting to iterate beyond end." );
if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ )
iterator.link_ = 0;
else
iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]);
iterator.itemIndex_ = 0;
}
void
ValueInternalMap::increment( IteratorState &iterator )
{
JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." );
++iterator.itemIndex_;
if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink )
{
JSON_ASSERT_MESSAGE( iterator.link_ != 0,
"ValueInternalMap::increment(): attempting to iterate beyond end." );
iterator.link_ = iterator.link_->next_;
if ( iterator.link_ == 0 )
incrementBucket( iterator );
}
else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() )
{
incrementBucket( iterator );
}
}
void
ValueInternalMap::decrement( IteratorState &iterator )
{
if ( iterator.itemIndex_ == 0 )
{
JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." );
if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] )
{
JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." );
--(iterator.bucketIndex_);
}
iterator.link_ = iterator.link_->previous_;
iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1;
}
}
const char *
ValueInternalMap::key( const IteratorState &iterator )
{
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
return iterator.link_->keys_[iterator.itemIndex_];
}
const char *
ValueInternalMap::key( const IteratorState &iterator, bool &isStatic )
{
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic();
return iterator.link_->keys_[iterator.itemIndex_];
}
Value &
ValueInternalMap::value( const IteratorState &iterator )
{
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
return iterator.link_->items_[iterator.itemIndex_];
}
int
ValueInternalMap::distance( const IteratorState &x, const IteratorState &y )
{
int offset = 0;
IteratorState it = x;
while ( !equals( it, y ) )
increment( it );
return offset;
}

View File

@@ -0,0 +1,885 @@
#include "reader.h"
#include "value.h"
#include <utility>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <iostream>
#include <stdexcept>
#if _MSC_VER >= 1400 // VC++ 8.0
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
#endif
namespace Json {
// Implementation of class Features
// ////////////////////////////////
Features::Features()
: allowComments_( true )
, strictRoot_( false )
{
}
Features
Features::all()
{
return Features();
}
Features
Features::strictMode()
{
Features features;
features.allowComments_ = false;
features.strictRoot_ = true;
return features;
}
// Implementation of class Reader
// ////////////////////////////////
static inline bool
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
{
return c == c1 || c == c2 || c == c3 || c == c4;
}
static inline bool
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
{
return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
}
static bool
containsNewLine( Reader::Location begin,
Reader::Location end )
{
for ( ;begin < end; ++begin )
if ( *begin == '\n' || *begin == '\r' )
return true;
return false;
}
static std::string codePointToUTF8(unsigned int cp)
{
std::string result;
// based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f)
{
result.resize(1);
result[0] = static_cast<char>(cp);
}
else if (cp <= 0x7FF)
{
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
}
else if (cp <= 0xFFFF)
{
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
}
else if (cp <= 0x10FFFF)
{
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
// Class Reader
// //////////////////////////////////////////////////////////////////
Reader::Reader()
: features_( Features::all() )
{
}
Reader::Reader( const Features &features )
: features_( features )
{
}
bool
Reader::parse( const std::string &document,
Value &root,
bool collectComments )
{
document_ = document;
const char *begin = document_.c_str();
const char *end = begin + document_.length();
return parse( begin, end, root, collectComments );
}
bool
Reader::parse( std::istream& sin,
Value &root,
bool collectComments )
{
//std::istream_iterator<char> begin(sin);
//std::istream_iterator<char> end;
// Those would allow streamed input from a file, if parse() were a
// template function.
// Since std::string is reference-counted, this at least does not
// create an extra copy.
std::string doc;
std::getline(sin, doc, (char)EOF);
return parse( doc, root, collectComments );
}
bool
Reader::parse( const char *beginDoc, const char *endDoc,
Value &root,
bool collectComments )
{
if ( !features_.allowComments_ )
{
collectComments = false;
}
begin_ = beginDoc;
end_ = endDoc;
collectComments_ = collectComments;
current_ = begin_;
lastValueEnd_ = 0;
lastValue_ = 0;
commentsBefore_ = "";
errors_.clear();
while ( !nodes_.empty() )
nodes_.pop();
nodes_.push( &root );
bool successful = readValue();
Token token;
skipCommentTokens( token );
if ( collectComments_ && !commentsBefore_.empty() )
root.setComment( commentsBefore_, commentAfter );
if ( features_.strictRoot_ )
{
if ( !root.isArray() && !root.isObject() )
{
// Set error location to start of doc, ideally should be first token found in doc
token.type_ = tokenError;
token.start_ = beginDoc;
token.end_ = endDoc;
addError( "A valid JSON document must be either an array or an object value.",
token );
return false;
}
}
return successful;
}
bool
Reader::readValue()
{
Token token;
skipCommentTokens( token );
bool successful = true;
if ( collectComments_ && !commentsBefore_.empty() )
{
currentValue().setComment( commentsBefore_, commentBefore );
commentsBefore_ = "";
}
switch ( token.type_ )
{
case tokenObjectBegin:
successful = readObject( token );
break;
case tokenArrayBegin:
successful = readArray( token );
break;
case tokenNumber:
successful = decodeNumber( token );
break;
case tokenString:
successful = decodeString( token );
break;
case tokenTrue:
currentValue() = true;
break;
case tokenFalse:
currentValue() = false;
break;
case tokenNull:
currentValue() = Value();
break;
default:
return addError( "Syntax error: value, object or array expected.", token );
}
if ( collectComments_ )
{
lastValueEnd_ = current_;
lastValue_ = &currentValue();
}
return successful;
}
void
Reader::skipCommentTokens( Token &token )
{
if ( features_.allowComments_ )
{
do
{
readToken( token );
}
while ( token.type_ == tokenComment );
}
else
{
readToken( token );
}
}
bool
Reader::expectToken( TokenType type, Token &token, const char *message )
{
readToken( token );
if ( token.type_ != type )
return addError( message, token );
return true;
}
bool
Reader::readToken( Token &token )
{
skipSpaces();
token.start_ = current_;
Char c = getNextChar();
bool ok = true;
switch ( c )
{
case '{':
token.type_ = tokenObjectBegin;
break;
case '}':
token.type_ = tokenObjectEnd;
break;
case '[':
token.type_ = tokenArrayBegin;
break;
case ']':
token.type_ = tokenArrayEnd;
break;
case '"':
token.type_ = tokenString;
ok = readString();
break;
case '/':
token.type_ = tokenComment;
ok = readComment();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
token.type_ = tokenNumber;
readNumber();
break;
case 't':
token.type_ = tokenTrue;
ok = match( "rue", 3 );
break;
case 'f':
token.type_ = tokenFalse;
ok = match( "alse", 4 );
break;
case 'n':
token.type_ = tokenNull;
ok = match( "ull", 3 );
break;
case ',':
token.type_ = tokenArraySeparator;
break;
case ':':
token.type_ = tokenMemberSeparator;
break;
case 0:
token.type_ = tokenEndOfStream;
break;
default:
ok = false;
break;
}
if ( !ok )
token.type_ = tokenError;
token.end_ = current_;
return true;
}
void
Reader::skipSpaces()
{
while ( current_ != end_ )
{
Char c = *current_;
if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' )
++current_;
else
break;
}
}
bool
Reader::match( Location pattern,
int patternLength )
{
if ( end_ - current_ < patternLength )
return false;
int index = patternLength;
while ( index-- )
if ( current_[index] != pattern[index] )
return false;
current_ += patternLength;
return true;
}
bool
Reader::readComment()
{
Location commentBegin = current_ - 1;
Char c = getNextChar();
bool successful = false;
if ( c == '*' )
successful = readCStyleComment();
else if ( c == '/' )
successful = readCppStyleComment();
if ( !successful )
return false;
if ( collectComments_ )
{
CommentPlacement placement = commentBefore;
if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) )
{
if ( c != '*' || !containsNewLine( commentBegin, current_ ) )
placement = commentAfterOnSameLine;
}
addComment( commentBegin, current_, placement );
}
return true;
}
void
Reader::addComment( Location begin,
Location end,
CommentPlacement placement )
{
assert( collectComments_ );
if ( placement == commentAfterOnSameLine )
{
assert( lastValue_ != 0 );
lastValue_->setComment( std::string( begin, end ), placement );
}
else
{
if ( !commentsBefore_.empty() )
commentsBefore_ += "\n";
commentsBefore_ += std::string( begin, end );
}
}
bool
Reader::readCStyleComment()
{
while ( current_ != end_ )
{
Char c = getNextChar();
if ( c == '*' && *current_ == '/' )
break;
}
return getNextChar() == '/';
}
bool
Reader::readCppStyleComment()
{
while ( current_ != end_ )
{
Char c = getNextChar();
if ( c == '\r' || c == '\n' )
break;
}
return true;
}
void
Reader::readNumber()
{
while ( current_ != end_ )
{
if ( !(*current_ >= '0' && *current_ <= '9') &&
!in( *current_, '.', 'e', 'E', '+', '-' ) )
break;
++current_;
}
}
bool
Reader::readString()
{
Char c = 0;
while ( current_ != end_ )
{
c = getNextChar();
if ( c == '\\' )
getNextChar();
else if ( c == '"' )
break;
}
return c == '"';
}
bool
Reader::readObject( Token &tokenStart )
{
Token tokenName;
std::string name;
currentValue() = Value( objectValue );
while ( readToken( tokenName ) )
{
bool initialTokenOk = true;
while ( tokenName.type_ == tokenComment && initialTokenOk )
initialTokenOk = readToken( tokenName );
if ( !initialTokenOk )
break;
if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object
return true;
if ( tokenName.type_ != tokenString )
break;
name = "";
if ( !decodeString( tokenName, name ) )
return recoverFromError( tokenObjectEnd );
Token colon;
if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator )
{
return addErrorAndRecover( "Missing ':' after object member name",
colon,
tokenObjectEnd );
}
Value &value = currentValue()[ name ];
nodes_.push( &value );
bool ok = readValue();
nodes_.pop();
if ( !ok ) // error already set
return recoverFromError( tokenObjectEnd );
Token comma;
if ( !readToken( comma )
|| ( comma.type_ != tokenObjectEnd &&
comma.type_ != tokenArraySeparator &&
comma.type_ != tokenComment ) )
{
return addErrorAndRecover( "Missing ',' or '}' in object declaration",
comma,
tokenObjectEnd );
}
bool finalizeTokenOk = true;
while ( comma.type_ == tokenComment &&
finalizeTokenOk )
finalizeTokenOk = readToken( comma );
if ( comma.type_ == tokenObjectEnd )
return true;
}
return addErrorAndRecover( "Missing '}' or object member name",
tokenName,
tokenObjectEnd );
}
bool
Reader::readArray( Token &tokenStart )
{
currentValue() = Value( arrayValue );
skipSpaces();
if ( *current_ == ']' ) // empty array
{
Token endArray;
readToken( endArray );
return true;
}
int index = 0;
while ( true )
{
Value &value = currentValue()[ index++ ];
nodes_.push( &value );
bool ok = readValue();
nodes_.pop();
if ( !ok ) // error already set
return recoverFromError( tokenArrayEnd );
Token token;
// Accept Comment after last item in the array.
ok = readToken( token );
while ( token.type_ == tokenComment && ok )
{
ok = readToken( token );
}
bool badTokenType = ( token.type_ == tokenArraySeparator &&
token.type_ == tokenArrayEnd );
if ( !ok || badTokenType )
{
return addErrorAndRecover( "Missing ',' or ']' in array declaration",
token,
tokenArrayEnd );
}
if ( token.type_ == tokenArrayEnd )
break;
}
return true;
}
bool
Reader::decodeNumber( Token &token )
{
bool isDouble = false;
for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
{
isDouble = isDouble
|| in( *inspect, '.', 'e', 'E', '+' )
|| ( *inspect == '-' && inspect != token.start_ );
}
if ( isDouble )
return decodeDouble( token );
Location current = token.start_;
bool isNegative = *current == '-';
if ( isNegative )
++current;
Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt)
: Value::maxUInt) / 10;
Value::UInt value = 0;
while ( current < token.end_ )
{
Char c = *current++;
if ( c < '0' || c > '9' )
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
if ( value >= threshold )
return decodeDouble( token );
value = value * 10 + Value::UInt(c - '0');
}
if ( isNegative )
currentValue() = -Value::Int( value );
else if ( value <= Value::UInt(Value::maxInt) )
currentValue() = Value::Int( value );
else
currentValue() = value;
return true;
}
bool
Reader::decodeDouble( Token &token )
{
double value = 0;
const int bufferSize = 32;
int count;
int length = int(token.end_ - token.start_);
if ( length <= bufferSize )
{
Char buffer[bufferSize];
memcpy( buffer, token.start_, length );
buffer[length] = 0;
count = sscanf( buffer, "%lf", &value );
}
else
{
std::string buffer( token.start_, token.end_ );
count = sscanf( buffer.c_str(), "%lf", &value );
}
if ( count != 1 )
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
currentValue() = value;
return true;
}
bool
Reader::decodeString( Token &token )
{
std::string decoded;
if ( !decodeString( token, decoded ) )
return false;
currentValue() = decoded;
return true;
}
bool
Reader::decodeString( Token &token, std::string &decoded )
{
decoded.reserve( token.end_ - token.start_ - 2 );
Location current = token.start_ + 1; // skip '"'
Location end = token.end_ - 1; // do not include '"'
while ( current != end )
{
Char c = *current++;
if ( c == '"' )
break;
else if ( c == '\\' )
{
if ( current == end )
return addError( "Empty escape sequence in string", token, current );
Char escape = *current++;
switch ( escape )
{
case '"': decoded += '"'; break;
case '/': decoded += '/'; break;
case '\\': decoded += '\\'; break;
case 'b': decoded += '\b'; break;
case 'f': decoded += '\f'; break;
case 'n': decoded += '\n'; break;
case 'r': decoded += '\r'; break;
case 't': decoded += '\t'; break;
case 'u':
{
unsigned int unicode;
if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
return false;
decoded += codePointToUTF8(unicode);
}
break;
default:
return addError( "Bad escape sequence in string", token, current );
}
}
else
{
decoded += c;
}
}
return true;
}
bool
Reader::decodeUnicodeCodePoint( Token &token,
Location &current,
Location end,
unsigned int &unicode )
{
if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
return false;
if (unicode >= 0xD800 && unicode <= 0xDBFF)
{
// surrogate pairs
if (end - current < 6)
return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
unsigned int surrogatePair;
if (*(current++) == '\\' && *(current++)== 'u')
{
if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
{
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
}
else
return false;
}
else
return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
}
return true;
}
bool
Reader::decodeUnicodeEscapeSequence( Token &token,
Location &current,
Location end,
unsigned int &unicode )
{
if ( end - current < 4 )
return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
unicode = 0;
for ( int index =0; index < 4; ++index )
{
Char c = *current++;
unicode *= 16;
if ( c >= '0' && c <= '9' )
unicode += c - '0';
else if ( c >= 'a' && c <= 'f' )
unicode += c - 'a' + 10;
else if ( c >= 'A' && c <= 'F' )
unicode += c - 'A' + 10;
else
return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
}
return true;
}
bool
Reader::addError( const std::string &message,
Token &token,
Location extra )
{
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = extra;
errors_.push_back( info );
return false;
}
bool
Reader::recoverFromError( TokenType skipUntilToken )
{
int errorCount = int(errors_.size());
Token skip;
while ( true )
{
if ( !readToken(skip) )
errors_.resize( errorCount ); // discard errors caused by recovery
if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream )
break;
}
errors_.resize( errorCount );
return false;
}
bool
Reader::addErrorAndRecover( const std::string &message,
Token &token,
TokenType skipUntilToken )
{
addError( message, token );
return recoverFromError( skipUntilToken );
}
Value &
Reader::currentValue()
{
return *(nodes_.top());
}
Reader::Char
Reader::getNextChar()
{
if ( current_ == end_ )
return 0;
return *current_++;
}
void
Reader::getLocationLineAndColumn( Location location,
int &line,
int &column ) const
{
Location current = begin_;
Location lastLineStart = current;
line = 0;
while ( current < location && current != end_ )
{
Char c = *current++;
if ( c == '\r' )
{
if ( *current == '\n' )
++current;
lastLineStart = current;
++line;
}
else if ( c == '\n' )
{
lastLineStart = current;
++line;
}
}
// column & line start at 1
column = int(location - lastLineStart) + 1;
++line;
}
std::string
Reader::getLocationLineAndColumn( Location location ) const
{
int line, column;
getLocationLineAndColumn( location, line, column );
char buffer[18+16+16+1];
sprintf( buffer, "Line %d, Column %d", line, column );
return buffer;
}
std::string
Reader::getFormatedErrorMessages() const
{
std::string formattedMessage;
for ( Errors::const_iterator itError = errors_.begin();
itError != errors_.end();
++itError )
{
const ErrorInfo &error = *itError;
formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
formattedMessage += " " + error.message_ + "\n";
if ( error.extra_ )
formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
}
return formattedMessage;
}
std::istream& operator>>( std::istream &sin, Value &root )
{
Json::Reader reader;
bool ok = reader.parse(sin, root, true);
//JSON_ASSERT( ok );
if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages());
return sin;
}
} // namespace Json

1724
src/cpp/json/json_value.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,292 @@
// included by json_value.cpp
// everything is within Json namespace
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIteratorBase
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIteratorBase::ValueIteratorBase()
#ifndef JSON_VALUE_USE_INTERNAL_MAP
: current_()
, isNull_( true )
{
}
#else
: isArray_( true )
, isNull_( true )
{
iterator_.array_ = ValueInternalArray::IteratorState();
}
#endif
#ifndef JSON_VALUE_USE_INTERNAL_MAP
ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator &current )
: current_( current )
, isNull_( false )
{
}
#else
ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state )
: isArray_( true )
{
iterator_.array_ = state;
}
ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state )
: isArray_( false )
{
iterator_.map_ = state;
}
#endif
Value &
ValueIteratorBase::deref() const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
return current_->second;
#else
if ( isArray_ )
return ValueInternalArray::dereference( iterator_.array_ );
return ValueInternalMap::value( iterator_.map_ );
#endif
}
void
ValueIteratorBase::increment()
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
++current_;
#else
if ( isArray_ )
ValueInternalArray::increment( iterator_.array_ );
ValueInternalMap::increment( iterator_.map_ );
#endif
}
void
ValueIteratorBase::decrement()
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
--current_;
#else
if ( isArray_ )
ValueInternalArray::decrement( iterator_.array_ );
ValueInternalMap::decrement( iterator_.map_ );
#endif
}
ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance( const SelfType &other ) const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
# ifdef JSON_USE_CPPTL_SMALLMAP
return current_ - other.current_;
# else
// Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default
// std::map::iterator. As begin() and end() are two instance
// of the default std::map::iterator, they can not be compared.
// To allow this, we handle this comparison specifically.
if ( isNull_ && other.isNull_ )
{
return 0;
}
// Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL,
// which is the one used by default).
// Using a portable hand-made version for non random iterator instead:
// return difference_type( std::distance( current_, other.current_ ) );
difference_type myDistance = 0;
for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it )
{
++myDistance;
}
return myDistance;
# endif
#else
if ( isArray_ )
return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ );
return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ );
#endif
}
bool
ValueIteratorBase::isEqual( const SelfType &other ) const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
if ( isNull_ )
{
return other.isNull_;
}
return current_ == other.current_;
#else
if ( isArray_ )
return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ );
return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ );
#endif
}
void
ValueIteratorBase::copy( const SelfType &other )
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
current_ = other.current_;
#else
if ( isArray_ )
iterator_.array_ = other.iterator_.array_;
iterator_.map_ = other.iterator_.map_;
#endif
}
Value
ValueIteratorBase::key() const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
const Value::CZString czstring = (*current_).first;
if ( czstring.c_str() )
{
if ( czstring.isStaticString() )
return Value( StaticString( czstring.c_str() ) );
return Value( czstring.c_str() );
}
return Value( czstring.index() );
#else
if ( isArray_ )
return Value( ValueInternalArray::indexOf( iterator_.array_ ) );
bool isStatic;
const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic );
if ( isStatic )
return Value( StaticString( memberName ) );
return Value( memberName );
#endif
}
UInt
ValueIteratorBase::index() const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
const Value::CZString czstring = (*current_).first;
if ( !czstring.c_str() )
return czstring.index();
return Value::UInt( -1 );
#else
if ( isArray_ )
return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) );
return Value::UInt( -1 );
#endif
}
const char *
ValueIteratorBase::memberName() const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
const char *name = (*current_).first.c_str();
return name ? name : "";
#else
if ( !isArray_ )
return ValueInternalMap::key( iterator_.map_ );
return "";
#endif
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueConstIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueConstIterator::ValueConstIterator()
{
}
#ifndef JSON_VALUE_USE_INTERNAL_MAP
ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator &current )
: ValueIteratorBase( current )
{
}
#else
ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state )
: ValueIteratorBase( state )
{
}
ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state )
: ValueIteratorBase( state )
{
}
#endif
ValueConstIterator &
ValueConstIterator::operator =( const ValueIteratorBase &other )
{
copy( other );
return *this;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIterator::ValueIterator()
{
}
#ifndef JSON_VALUE_USE_INTERNAL_MAP
ValueIterator::ValueIterator( const Value::ObjectValues::iterator &current )
: ValueIteratorBase( current )
{
}
#else
ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state )
: ValueIteratorBase( state )
{
}
ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state )
: ValueIteratorBase( state )
{
}
#endif
ValueIterator::ValueIterator( const ValueConstIterator &other )
: ValueIteratorBase( other )
{
}
ValueIterator::ValueIterator( const ValueIterator &other )
: ValueIteratorBase( other )
{
}
ValueIterator &
ValueIterator::operator =( const SelfType &other )
{
copy( other );
return *this;
}

View File

@@ -0,0 +1,829 @@
#include "writer.h"
#include <utility>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#if _MSC_VER >= 1400 // VC++ 8.0
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
#endif
namespace Json {
static bool isControlCharacter(char ch)
{
return ch > 0 && ch <= 0x1F;
}
static bool containsControlCharacter( const char* str )
{
while ( *str )
{
if ( isControlCharacter( *(str++) ) )
return true;
}
return false;
}
static void uintToString( unsigned int value,
char *&current )
{
*--current = 0;
do
{
*--current = (value % 10) + '0';
value /= 10;
}
while ( value != 0 );
}
std::string valueToString( Int value )
{
char buffer[32];
char *current = buffer + sizeof(buffer);
bool isNegative = value < 0;
if ( isNegative )
value = -value;
uintToString( UInt(value), current );
if ( isNegative )
*--current = '-';
assert( current >= buffer );
return current;
}
std::string valueToString( UInt value )
{
char buffer[32];
char *current = buffer + sizeof(buffer);
uintToString( value, current );
assert( current >= buffer );
return current;
}
std::string valueToString( double value )
{
char buffer[32];
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
#else
sprintf(buffer, "%#.16g", value);
#endif
char* ch = buffer + strlen(buffer) - 1;
if (*ch != '0') return buffer; // nothing to truncate, so save time
while(ch > buffer && *ch == '0'){
--ch;
}
char* last_nonzero = ch;
while(ch >= buffer){
switch(*ch){
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
--ch;
continue;
case '.':
// Truncate zeroes to save bytes in output, but keep one.
*(last_nonzero+2) = '\0';
return buffer;
default:
return buffer;
}
}
return buffer;
}
std::string valueToString( bool value )
{
return value ? "true" : "false";
}
std::string valueToQuotedString( const char *value )
{
// Not sure how to handle unicode...
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
return std::string("\"") + value + "\"";
// We have to walk value and escape any special characters.
// Appending to std::string is not efficient, but this should be rare.
// (Note: forward slashes are *not* rare, but I am not escaping them.)
unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
std::string result;
result.reserve(maxsize); // to avoid lots of mallocs
result += "\"";
for (const char* c=value; *c != 0; ++c)
{
switch(*c)
{
case '\"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
//case '/':
// Even though \/ is considered a legal escape in JSON, a bare
// slash is also legal, so I see no reason to escape it.
// (I hope I am not misunderstanding something.
// blep notes: actually escaping \/ may be useful in javascript to avoid </
// sequence.
// Should add a flag to allow this compatibility mode and prevent this
// sequence from occurring.
default:
if ( isControlCharacter( *c ) )
{
std::ostringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
result += oss.str();
}
else
{
result += *c;
}
break;
}
}
result += "\"";
return result;
}
// Class Writer
// //////////////////////////////////////////////////////////////////
Writer::~Writer()
{
}
// Class FastWriter
// //////////////////////////////////////////////////////////////////
FastWriter::FastWriter()
: yamlCompatiblityEnabled_( false )
{
}
void
FastWriter::enableYAMLCompatibility()
{
yamlCompatiblityEnabled_ = true;
}
std::string
FastWriter::write( const Value &root )
{
document_ = "";
writeValue( root );
document_ += "\n";
return document_;
}
void
FastWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue:
document_ += "null";
break;
case intValue:
document_ += valueToString( value.asInt() );
break;
case uintValue:
document_ += valueToString( value.asUInt() );
break;
case realValue:
document_ += valueToString( value.asDouble() );
break;
case stringValue:
document_ += valueToQuotedString( value.asCString() );
break;
case booleanValue:
document_ += valueToString( value.asBool() );
break;
case arrayValue:
{
document_ += "[";
int size = value.size();
for ( int index =0; index < size; ++index )
{
if ( index > 0 )
document_ += ",";
writeValue( value[index] );
}
document_ += "]";
}
break;
case objectValue:
{
Value::Members members( value.getMemberNames() );
document_ += "{";
for ( Value::Members::iterator it = members.begin();
it != members.end();
++it )
{
const std::string &name = *it;
if ( it != members.begin() )
document_ += ",";
document_ += valueToQuotedString( name.c_str() );
document_ += yamlCompatiblityEnabled_ ? ": "
: ":";
writeValue( value[name] );
}
document_ += "}";
}
break;
}
}
// Class StyledWriter
// //////////////////////////////////////////////////////////////////
StyledWriter::StyledWriter()
: rightMargin_( 74 )
, indentSize_( 3 )
{
}
std::string
StyledWriter::write( const Value &root )
{
document_ = "";
addChildValues_ = false;
indentString_ = "";
writeCommentBeforeValue( root );
writeValue( root );
writeCommentAfterValueOnSameLine( root );
document_ += "\n";
return document_;
}
void
StyledWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue:
pushValue( "null" );
break;
case intValue:
pushValue( valueToString( value.asInt() ) );
break;
case uintValue:
pushValue( valueToString( value.asUInt() ) );
break;
case realValue:
pushValue( valueToString( value.asDouble() ) );
break;
case stringValue:
pushValue( valueToQuotedString( value.asCString() ) );
break;
case booleanValue:
pushValue( valueToString( value.asBool() ) );
break;
case arrayValue:
writeArrayValue( value);
break;
case objectValue:
{
Value::Members members( value.getMemberNames() );
if ( members.empty() )
pushValue( "{}" );
else
{
writeWithIndent( "{" );
indent();
Value::Members::iterator it = members.begin();
while ( true )
{
const std::string &name = *it;
const Value &childValue = value[name];
writeCommentBeforeValue( childValue );
writeWithIndent( valueToQuotedString( name.c_str() ) );
document_ += " : ";
writeValue( childValue );
if ( ++it == members.end() )
{
writeCommentAfterValueOnSameLine( childValue );
break;
}
document_ += ",";
writeCommentAfterValueOnSameLine( childValue );
}
unindent();
writeWithIndent( "}" );
}
}
break;
}
}
void
StyledWriter::writeArrayValue( const Value &value )
{
unsigned size = value.size();
if ( size == 0 )
pushValue( "[]" );
else
{
bool isArrayMultiLine = isMultineArray( value );
if ( isArrayMultiLine )
{
writeWithIndent( "[" );
indent();
bool hasChildValue = !childValues_.empty();
unsigned index =0;
while ( true )
{
const Value &childValue = value[index];
writeCommentBeforeValue( childValue );
if ( hasChildValue )
writeWithIndent( childValues_[index] );
else
{
writeIndent();
writeValue( childValue );
}
if ( ++index == size )
{
writeCommentAfterValueOnSameLine( childValue );
break;
}
document_ += ",";
writeCommentAfterValueOnSameLine( childValue );
}
unindent();
writeWithIndent( "]" );
}
else // output on a single line
{
assert( childValues_.size() == size );
document_ += "[ ";
for ( unsigned index =0; index < size; ++index )
{
if ( index > 0 )
document_ += ", ";
document_ += childValues_[index];
}
document_ += " ]";
}
}
}
bool
StyledWriter::isMultineArray( const Value &value )
{
int size = value.size();
bool isMultiLine = size*3 >= rightMargin_ ;
childValues_.clear();
for ( int index =0; index < size && !isMultiLine; ++index )
{
const Value &childValue = value[index];
isMultiLine = isMultiLine ||
( (childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0 );
}
if ( !isMultiLine ) // check if line length > max line length
{
childValues_.reserve( size );
addChildValues_ = true;
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
for ( int index =0; index < size && !isMultiLine; ++index )
{
writeValue( value[index] );
lineLength += int( childValues_[index].length() );
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
}
addChildValues_ = false;
isMultiLine = isMultiLine || lineLength >= rightMargin_;
}
return isMultiLine;
}
void
StyledWriter::pushValue( const std::string &value )
{
if ( addChildValues_ )
childValues_.push_back( value );
else
document_ += value;
}
void
StyledWriter::writeIndent()
{
if ( !document_.empty() )
{
char last = document_[document_.length()-1];
if ( last == ' ' ) // already indented
return;
if ( last != '\n' ) // Comments may add new-line
document_ += '\n';
}
document_ += indentString_;
}
void
StyledWriter::writeWithIndent( const std::string &value )
{
writeIndent();
document_ += value;
}
void
StyledWriter::indent()
{
indentString_ += std::string( indentSize_, ' ' );
}
void
StyledWriter::unindent()
{
assert( int(indentString_.size()) >= indentSize_ );
indentString_.resize( indentString_.size() - indentSize_ );
}
void
StyledWriter::writeCommentBeforeValue( const Value &root )
{
if ( !root.hasComment( commentBefore ) )
return;
document_ += normalizeEOL( root.getComment( commentBefore ) );
document_ += "\n";
}
void
StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
{
if ( root.hasComment( commentAfterOnSameLine ) )
document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
if ( root.hasComment( commentAfter ) )
{
document_ += "\n";
document_ += normalizeEOL( root.getComment( commentAfter ) );
document_ += "\n";
}
}
bool
StyledWriter::hasCommentForValue( const Value &value )
{
return value.hasComment( commentBefore )
|| value.hasComment( commentAfterOnSameLine )
|| value.hasComment( commentAfter );
}
std::string
StyledWriter::normalizeEOL( const std::string &text )
{
std::string normalized;
normalized.reserve( text.length() );
const char *begin = text.c_str();
const char *end = begin + text.length();
const char *current = begin;
while ( current != end )
{
char c = *current++;
if ( c == '\r' ) // mac or dos EOL
{
if ( *current == '\n' ) // convert dos EOL
++current;
normalized += '\n';
}
else // handle unix EOL & other char
normalized += c;
}
return normalized;
}
// Class StyledStreamWriter
// //////////////////////////////////////////////////////////////////
StyledStreamWriter::StyledStreamWriter( std::string indentation )
: document_(NULL)
, rightMargin_( 74 )
, indentation_( indentation )
{
}
void
StyledStreamWriter::write( std::ostream &out, const Value &root )
{
document_ = &out;
addChildValues_ = false;
indentString_ = "";
writeCommentBeforeValue( root );
writeValue( root );
writeCommentAfterValueOnSameLine( root );
*document_ << "\n";
document_ = NULL; // Forget the stream, for safety.
}
void
StyledStreamWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue:
pushValue( "null" );
break;
case intValue:
pushValue( valueToString( value.asInt() ) );
break;
case uintValue:
pushValue( valueToString( value.asUInt() ) );
break;
case realValue:
pushValue( valueToString( value.asDouble() ) );
break;
case stringValue:
pushValue( valueToQuotedString( value.asCString() ) );
break;
case booleanValue:
pushValue( valueToString( value.asBool() ) );
break;
case arrayValue:
writeArrayValue( value);
break;
case objectValue:
{
Value::Members members( value.getMemberNames() );
if ( members.empty() )
pushValue( "{}" );
else
{
writeWithIndent( "{" );
indent();
Value::Members::iterator it = members.begin();
while ( true )
{
const std::string &name = *it;
const Value &childValue = value[name];
writeCommentBeforeValue( childValue );
writeWithIndent( valueToQuotedString( name.c_str() ) );
*document_ << " : ";
writeValue( childValue );
if ( ++it == members.end() )
{
writeCommentAfterValueOnSameLine( childValue );
break;
}
*document_ << ",";
writeCommentAfterValueOnSameLine( childValue );
}
unindent();
writeWithIndent( "}" );
}
}
break;
}
}
void
StyledStreamWriter::writeArrayValue( const Value &value )
{
unsigned size = value.size();
if ( size == 0 )
pushValue( "[]" );
else
{
bool isArrayMultiLine = isMultineArray( value );
if ( isArrayMultiLine )
{
writeWithIndent( "[" );
indent();
bool hasChildValue = !childValues_.empty();
unsigned index =0;
while ( true )
{
const Value &childValue = value[index];
writeCommentBeforeValue( childValue );
if ( hasChildValue )
writeWithIndent( childValues_[index] );
else
{
writeIndent();
writeValue( childValue );
}
if ( ++index == size )
{
writeCommentAfterValueOnSameLine( childValue );
break;
}
*document_ << ",";
writeCommentAfterValueOnSameLine( childValue );
}
unindent();
writeWithIndent( "]" );
}
else // output on a single line
{
assert( childValues_.size() == size );
*document_ << "[ ";
for ( unsigned index =0; index < size; ++index )
{
if ( index > 0 )
*document_ << ", ";
*document_ << childValues_[index];
}
*document_ << " ]";
}
}
}
bool
StyledStreamWriter::isMultineArray( const Value &value )
{
int size = value.size();
bool isMultiLine = size*3 >= rightMargin_ ;
childValues_.clear();
for ( int index =0; index < size && !isMultiLine; ++index )
{
const Value &childValue = value[index];
isMultiLine = isMultiLine ||
( (childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0 );
}
if ( !isMultiLine ) // check if line length > max line length
{
childValues_.reserve( size );
addChildValues_ = true;
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
for ( int index =0; index < size && !isMultiLine; ++index )
{
writeValue( value[index] );
lineLength += int( childValues_[index].length() );
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
}
addChildValues_ = false;
isMultiLine = isMultiLine || lineLength >= rightMargin_;
}
return isMultiLine;
}
void
StyledStreamWriter::pushValue( const std::string &value )
{
if ( addChildValues_ )
childValues_.push_back( value );
else
*document_ << value;
}
void
StyledStreamWriter::writeIndent()
{
/*
Some comments in this method would have been nice. ;-)
if ( !document_.empty() )
{
char last = document_[document_.length()-1];
if ( last == ' ' ) // already indented
return;
if ( last != '\n' ) // Comments may add new-line
*document_ << '\n';
}
*/
*document_ << '\n' << indentString_;
}
void
StyledStreamWriter::writeWithIndent( const std::string &value )
{
writeIndent();
*document_ << value;
}
void
StyledStreamWriter::indent()
{
indentString_ += indentation_;
}
void
StyledStreamWriter::unindent()
{
assert( indentString_.size() >= indentation_.size() );
indentString_.resize( indentString_.size() - indentation_.size() );
}
void
StyledStreamWriter::writeCommentBeforeValue( const Value &root )
{
if ( !root.hasComment( commentBefore ) )
return;
*document_ << normalizeEOL( root.getComment( commentBefore ) );
*document_ << "\n";
}
void
StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
{
if ( root.hasComment( commentAfterOnSameLine ) )
*document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
if ( root.hasComment( commentAfter ) )
{
*document_ << "\n";
*document_ << normalizeEOL( root.getComment( commentAfter ) );
*document_ << "\n";
}
}
bool
StyledStreamWriter::hasCommentForValue( const Value &value )
{
return value.hasComment( commentBefore )
|| value.hasComment( commentAfterOnSameLine )
|| value.hasComment( commentAfter );
}
std::string
StyledStreamWriter::normalizeEOL( const std::string &text )
{
std::string normalized;
normalized.reserve( text.length() );
const char *begin = text.c_str();
const char *end = begin + text.length();
const char *current = begin;
while ( current != end )
{
char c = *current++;
if ( c == '\r' ) // mac or dos EOL
{
if ( *current == '\n' ) // convert dos EOL
++current;
normalized += '\n';
}
else // handle unix EOL & other char
normalized += c;
}
return normalized;
}
std::ostream& operator<<( std::ostream &sout, const Value &root )
{
Json::StyledStreamWriter writer;
writer.write(sout, root);
return sout;
}
} // namespace Json

196
src/cpp/json/reader.h Normal file
View File

@@ -0,0 +1,196 @@
#ifndef CPPTL_JSON_READER_H_INCLUDED
# define CPPTL_JSON_READER_H_INCLUDED
# include "features.h"
# include "value.h"
# include <deque>
# include <stack>
# include <string>
# include <iostream>
namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value.
*
*/
class JSON_API Reader
{
public:
typedef char Char;
typedef const Char *Location;
/** \brief Constructs a Reader allowing all features
* for parsing.
*/
Reader();
/** \brief Constructs a Reader allowing the specified feature set
* for parsing.
*/
Reader( const Features &features );
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
* \param document UTF-8 encoded string containing the document to read.
* \param root [out] Contains the root value of the document if it was
* successfully parsed.
* \param collectComments \c true to collect comment and allow writing them back during
* serialization, \c false to discard comments.
* This parameter is ignored if Features::allowComments_
* is \c false.
* \return \c true if the document was successfully parsed, \c false if an error occurred.
*/
bool parse( const std::string &document,
Value &root,
bool collectComments = true );
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
* \param document UTF-8 encoded string containing the document to read.
* \param root [out] Contains the root value of the document if it was
* successfully parsed.
* \param collectComments \c true to collect comment and allow writing them back during
* serialization, \c false to discard comments.
* This parameter is ignored if Features::allowComments_
* is \c false.
* \return \c true if the document was successfully parsed, \c false if an error occurred.
*/
bool parse( const char *beginDoc, const char *endDoc,
Value &root,
bool collectComments = true );
/// \brief Parse from input stream.
/// \see Json::operator>>(std::istream&, Json::Value&).
bool parse( std::istream &is,
Value &root,
bool collectComments = true );
/** \brief Returns a user friendly string that list errors in the parsed document.
* \return Formatted error message with the list of errors with their location in
* the parsed document. An empty string is returned if no error occurred
* during parsing.
*/
std::string getFormatedErrorMessages() const;
private:
enum TokenType
{
tokenEndOfStream = 0,
tokenObjectBegin,
tokenObjectEnd,
tokenArrayBegin,
tokenArrayEnd,
tokenString,
tokenNumber,
tokenTrue,
tokenFalse,
tokenNull,
tokenArraySeparator,
tokenMemberSeparator,
tokenComment,
tokenError
};
class Token
{
public:
TokenType type_;
Location start_;
Location end_;
};
class ErrorInfo
{
public:
Token token_;
std::string message_;
Location extra_;
};
typedef std::deque<ErrorInfo> Errors;
bool expectToken( TokenType type, Token &token, const char *message );
bool readToken( Token &token );
void skipSpaces();
bool match( Location pattern,
int patternLength );
bool readComment();
bool readCStyleComment();
bool readCppStyleComment();
bool readString();
void readNumber();
bool readValue();
bool readObject( Token &token );
bool readArray( Token &token );
bool decodeNumber( Token &token );
bool decodeString( Token &token );
bool decodeString( Token &token, std::string &decoded );
bool decodeDouble( Token &token );
bool decodeUnicodeCodePoint( Token &token,
Location &current,
Location end,
unsigned int &unicode );
bool decodeUnicodeEscapeSequence( Token &token,
Location &current,
Location end,
unsigned int &unicode );
bool addError( const std::string &message,
Token &token,
Location extra = 0 );
bool recoverFromError( TokenType skipUntilToken );
bool addErrorAndRecover( const std::string &message,
Token &token,
TokenType skipUntilToken );
void skipUntilSpace();
Value &currentValue();
Char getNextChar();
void getLocationLineAndColumn( Location location,
int &line,
int &column ) const;
std::string getLocationLineAndColumn( Location location ) const;
void addComment( Location begin,
Location end,
CommentPlacement placement );
void skipCommentTokens( Token &token );
typedef std::stack<Value *> Nodes;
Nodes nodes_;
Errors errors_;
std::string document_;
Location begin_;
Location end_;
Location current_;
Location lastValueEnd_;
Value *lastValue_;
std::string commentsBefore_;
Features features_;
bool collectComments_;
};
/** \brief Read from 'sin' into 'root'.
Always keep comments from the input JSON.
This can be used to read a file into a particular sub-object.
For example:
\code
Json::Value root;
cin >> root["dir"]["file"];
cout << root;
\endcode
Result:
\verbatim
{
"dir": {
"file": {
// The input stream JSON would be nested here.
}
}
}
\endverbatim
\throw std::exception on parse error.
\see Json::operator<<()
*/
std::istream& operator>>( std::istream&, Value& );
} // namespace Json
#endif // CPPTL_JSON_READER_H_INCLUDED

1069
src/cpp/json/value.h Normal file

File diff suppressed because it is too large Load Diff

1
src/cpp/json/version Normal file
View File

@@ -0,0 +1 @@
0.5.0

174
src/cpp/json/writer.h Normal file
View File

@@ -0,0 +1,174 @@
#ifndef JSON_WRITER_H_INCLUDED
# define JSON_WRITER_H_INCLUDED
# include "value.h"
# include <vector>
# include <string>
# include <iostream>
namespace Json {
class Value;
/** \brief Abstract class for writers.
*/
class JSON_API Writer
{
public:
virtual ~Writer();
virtual std::string write( const Value &root ) = 0;
};
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly).
*
* The JSON document is written in a single line. It is not intended for 'human' consumption,
* but may be usefull to support feature such as RPC where bandwith is limited.
* \sa Reader, Value
*/
class JSON_API FastWriter : public Writer
{
public:
FastWriter();
virtual ~FastWriter(){}
void enableYAMLCompatibility();
public: // overridden from Writer
virtual std::string write( const Value &root );
private:
void writeValue( const Value &value );
std::string document_;
bool yamlCompatiblityEnabled_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value types,
* and all the values fit on one lines, then print the array on a single line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their #CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
*/
class JSON_API StyledWriter: public Writer
{
public:
StyledWriter();
virtual ~StyledWriter(){}
public: // overridden from Writer
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param root Value to serialize.
* \return String containing the JSON document that represents the root value.
*/
virtual std::string write( const Value &root );
private:
void writeValue( const Value &value );
void writeArrayValue( const Value &value );
bool isMultineArray( const Value &value );
void pushValue( const std::string &value );
void writeIndent();
void writeWithIndent( const std::string &value );
void indent();
void unindent();
void writeCommentBeforeValue( const Value &root );
void writeCommentAfterValueOnSameLine( const Value &root );
bool hasCommentForValue( const Value &value );
static std::string normalizeEOL( const std::string &text );
typedef std::vector<std::string> ChildValues;
ChildValues childValues_;
std::string document_;
std::string indentString_;
int rightMargin_;
int indentSize_;
bool addChildValues_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
to a stream rather than to a string.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value types,
* and all the values fit on one lines, then print the array on a single line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their #CommentPlacement.
*
* \param indentation Each level will be indented by this amount extra.
* \sa Reader, Value, Value::setComment()
*/
class JSON_API StyledStreamWriter
{
public:
StyledStreamWriter( std::string indentation="\t" );
~StyledStreamWriter(){}
public:
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param out Stream to write to. (Can be ostringstream, e.g.)
* \param root Value to serialize.
* \note There is no point in deriving from Writer, since write() should not return a value.
*/
void write( std::ostream &out, const Value &root );
private:
void writeValue( const Value &value );
void writeArrayValue( const Value &value );
bool isMultineArray( const Value &value );
void pushValue( const std::string &value );
void writeIndent();
void writeWithIndent( const std::string &value );
void indent();
void unindent();
void writeCommentBeforeValue( const Value &root );
void writeCommentAfterValueOnSameLine( const Value &root );
bool hasCommentForValue( const Value &value );
static std::string normalizeEOL( const std::string &text );
typedef std::vector<std::string> ChildValues;
ChildValues childValues_;
std::ostream* document_;
std::string indentString_;
int rightMargin_;
std::string indentation_;
bool addChildValues_;
};
std::string JSON_API valueToString( Int value );
std::string JSON_API valueToString( UInt value );
std::string JSON_API valueToString( double value );
std::string JSON_API valueToString( bool value );
std::string JSON_API valueToQuotedString( const char *value );
/// \brief Output using the StyledStreamWriter.
/// \see Json::operator>>()
std::ostream& operator<<( std::ostream&, const Value &root );
} // namespace Json
#endif // JSON_WRITER_H_INCLUDED

View File

@@ -0,0 +1,67 @@
#include "AccountState.h"
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/algorithm/string.hpp>
#include "../json/writer.h"
#include "Ledger.h"
#include "Serializer.h"
#include "Log.h"
AccountState::AccountState(const RippleAddress& naAccountID) : mAccountID(naAccountID), mValid(false)
{
if (!naAccountID.isValid()) return;
mLedgerEntry = boost::make_shared<SerializedLedgerEntry>(ltACCOUNT_ROOT);
mLedgerEntry->setIndex(Ledger::getAccountRootIndex(naAccountID));
mLedgerEntry->setFieldAccount(sfAccount, naAccountID.getAccountID());
mValid = true;
}
AccountState::AccountState(SLE::ref ledgerEntry, const RippleAddress& naAccountID) :
mAccountID(naAccountID), mLedgerEntry(ledgerEntry), mValid(false)
{
if (!mLedgerEntry)
return;
if (mLedgerEntry->getType() != ltACCOUNT_ROOT)
return;
mValid = true;
}
std::string AccountState::createGravatarUrl(uint128 uEmailHash)
{
std::vector<unsigned char> vucMD5(uEmailHash.begin(), uEmailHash.end());
std::string strMD5Lower = strHex(vucMD5);
boost::to_lower(strMD5Lower);
return str(boost::format("http://www.gravatar.com/avatar/%s") % strMD5Lower);
}
void AccountState::addJson(Json::Value& val)
{
val = mLedgerEntry->getJson(0);
if (mValid)
{
if (mLedgerEntry->isFieldPresent(sfEmailHash))
val["UrlGravatar"] = createGravatarUrl(mLedgerEntry->getFieldH128(sfEmailHash));
}
else
{
val["Invalid"] = true;
}
}
void AccountState::dump()
{
Json::Value j(Json::objectValue);
addJson(j);
Log(lsINFO) << j;
}
// vim:ts=4

View File

@@ -0,0 +1,59 @@
#ifndef __ACCOUNTSTATE__
#define __ACCOUNTSTATE__
//
// Provide abstract access to an account's state, such that access to the serialized format is hidden.
//
#include <vector>
#include <boost/shared_ptr.hpp>
#include "../json/value.h"
#include "types.h"
#include "RippleAddress.h"
#include "SerializedLedger.h"
class AccountState
{
public:
typedef boost::shared_ptr<AccountState> pointer;
private:
RippleAddress mAccountID;
RippleAddress mAuthorizedKey;
SerializedLedgerEntry::pointer mLedgerEntry;
bool mValid;
public:
AccountState(const RippleAddress& naAccountID); // For new accounts
AccountState(SLE::ref ledgerEntry,const RippleAddress& naAccountI); // For accounts in a ledger
bool bHaveAuthorizedKey()
{
return mLedgerEntry->isFieldPresent(sfAuthorizedKey);
}
RippleAddress getAuthorizedKey()
{
return mLedgerEntry->getFieldAccount(sfAuthorizedKey);
}
STAmount getBalance() const { return mLedgerEntry->getFieldAmount(sfBalance); }
uint32 getSeq() const { return mLedgerEntry->getFieldU32(sfSequence); }
SerializedLedgerEntry::pointer getSLE() { return mLedgerEntry; }
const SerializedLedgerEntry& peekSLE() const { return *mLedgerEntry; }
SerializedLedgerEntry& peekSLE() { return *mLedgerEntry; }
std::vector<unsigned char> getRaw() const;
void addJson(Json::Value& value);
void dump();
static std::string createGravatarUrl(uint128 uEmailHash);
};
#endif
// vim:ts=4

1426
src/cpp/ripple/Amount.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,308 @@
#include "Application.h"
#include "Config.h"
#include "PeerDoor.h"
#include "RPCDoor.h"
#include "BitcoinUtil.h"
#include "key.h"
#include "utils.h"
#include "TaggedCache.h"
#include "Log.h"
#include "../database/SqliteDatabase.h"
#include <iostream>
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
#include <boost/thread.hpp>
SETUP_LOG();
LogPartition TaggedCachePartition("TaggedCache");
Application* theApp = NULL;
DatabaseCon::DatabaseCon(const std::string& strName, const char *initStrings[], int initCount)
{
boost::filesystem::path pPath = theConfig.DATA_DIR / strName;
mDatabase = new SqliteDatabase(pPath.string().c_str());
mDatabase->connect();
for(int i = 0; i < initCount; ++i)
mDatabase->executeSQL(initStrings[i], true);
}
DatabaseCon::~DatabaseCon()
{
mDatabase->disconnect();
delete mDatabase;
}
Application::Application() :
mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mMasterLedger),
mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300),
mSNTPClient(mAuxService), mRPCHandler(&mNetOps),
mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL),
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL),
mSweepTimer(mAuxService)
{
RAND_bytes(mNonce256.begin(), mNonce256.size());
RAND_bytes(reinterpret_cast<unsigned char *>(&mNonceST), sizeof(mNonceST));
mJobQueue.setThreadCount();
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
}
extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], *NetNodeDBInit[];
extern int RpcDBCount, TxnDBCount, LedgerDBCount, WalletDBCount, HashNodeDBCount, NetNodeDBCount;
void Application::stop()
{
mIOService.stop();
mJobQueue.shutdown();
mHashedObjectStore.bulkWrite();
mValidations.flush();
mAuxService.stop();
cLog(lsINFO) << "Stopped: " << mIOService.stopped();
}
static void InitDB(DatabaseCon** dbCon, const char *fileName, const char *dbInit[], int dbCount)
{
*dbCon = new DatabaseCon(fileName, dbInit, dbCount);
}
void Application::run()
{
assert(mTxnDB == NULL);
if (!theConfig.DEBUG_LOGFILE.empty())
{ // Let DEBUG messages go to the file but only WARNING or higher to regular output (unless verbose)
Log::setLogFile(theConfig.DEBUG_LOGFILE);
if (Log::getMinSeverity() > lsDEBUG)
LogPartition::setSeverity(lsDEBUG);
}
boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService));
auxThread.detach();
if (!theConfig.RUN_STANDALONE)
mSNTPClient.init(theConfig.SNTP_SERVERS);
//
// Construct databases.
//
boost::thread t1(boost::bind(&InitDB, &mRpcDB, "rpc.db", RpcDBInit, RpcDBCount));
boost::thread t2(boost::bind(&InitDB, &mTxnDB, "transaction.db", TxnDBInit, TxnDBCount));
boost::thread t3(boost::bind(&InitDB, &mLedgerDB, "ledger.db", LedgerDBInit, LedgerDBCount));
boost::thread t4(boost::bind(&InitDB, &mWalletDB, "wallet.db", WalletDBInit, WalletDBCount));
boost::thread t5(boost::bind(&InitDB, &mHashNodeDB, "hashnode.db", HashNodeDBInit, HashNodeDBCount));
boost::thread t6(boost::bind(&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount));
t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); t6.join();
if (theConfig.START_UP == Config::FRESH)
{
cLog(lsINFO) << "Starting new Ledger";
startNewLedger();
}
else if (theConfig.START_UP == Config::LOAD)
{
cLog(lsINFO) << "Loading Old Ledger";
loadOldLedger();
}
else if (theConfig.START_UP == Config::NETWORK)
{ // This should probably become the default once we have a stable network
if (!theConfig.RUN_STANDALONE)
mNetOps.needNetworkLedger();
startNewLedger();
}
else
startNewLedger();
if (theConfig.FULL_HISTORY && (theConfig.START_UP != Config::LOAD))
{
Ledger::pointer ledger = Ledger::getLastFullLedger();
if (ledger)
mMasterLedger.setLedgerRangePresent(0, ledger->getLedgerSeq());
}
//
// Begin validation and ip maintenance.
// - Wallet maintains local information: including identity and network connection persistence information.
//
mWallet.start();
//
// Set up UNL.
//
if (!theConfig.RUN_STANDALONE)
getUNL().nodeBootstrap();
//
// Allow peer connections.
//
if (!theConfig.RUN_STANDALONE && !theConfig.PEER_IP.empty() && theConfig.PEER_PORT)
{
mPeerDoor = new PeerDoor(mIOService);
}
else
{
cLog(lsINFO) << "Peer interface: disabled";
}
//
// Allow RPC connections.
//
if (!theConfig.RPC_IP.empty() && theConfig.RPC_PORT)
{
mRPCDoor = new RPCDoor(mIOService);
}
else
{
cLog(lsINFO) << "RPC interface: disabled";
}
//
// Allow private WS connections.
//
if (!theConfig.WEBSOCKET_IP.empty() && theConfig.WEBSOCKET_PORT)
{
mWSPrivateDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_IP, theConfig.WEBSOCKET_PORT, false);
}
else
{
cLog(lsINFO) << "WS private interface: disabled";
}
//
// Allow public WS connections.
//
if (!theConfig.WEBSOCKET_PUBLIC_IP.empty() && theConfig.WEBSOCKET_PUBLIC_PORT)
{
mWSPublicDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_PUBLIC_IP, theConfig.WEBSOCKET_PUBLIC_PORT, true);
}
else
{
cLog(lsINFO) << "WS public interface: disabled";
}
//
// Begin connecting to network.
//
if (!theConfig.RUN_STANDALONE)
mConnectionPool.start();
if (theConfig.RUN_STANDALONE)
{
cLog(lsWARNING) << "Running in standalone mode";
mNetOps.setStandAlone();
}
else
mNetOps.setStateTimer();
mIOService.run(); // This blocks
if (mWSPublicDoor)
mWSPublicDoor->stop();
if (mWSPrivateDoor)
mWSPrivateDoor->stop();
cLog(lsINFO) << "Done.";
}
void Application::sweep()
{
mMasterTransaction.sweep();
mHashedObjectStore.sweep();
mMasterLedger.sweep();
mTempNodeCache.sweep();
mValidations.sweep();
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
}
Application::~Application()
{
delete mTxnDB;
delete mLedgerDB;
delete mWalletDB;
delete mHashNodeDB;
delete mNetNodeDB;
}
void Application::startNewLedger()
{
// New stuff.
RippleAddress rootSeedMaster = RippleAddress::createSeedGeneric("masterpassphrase");
RippleAddress rootGeneratorMaster = RippleAddress::createGeneratorPublic(rootSeedMaster);
RippleAddress rootAddress = RippleAddress::createAccountPublic(rootGeneratorMaster, 0);
// Print enough information to be able to claim root account.
cLog(lsINFO) << "Root master seed: " << rootSeedMaster.humanSeed();
cLog(lsINFO) << "Root account: " << rootAddress.humanAccountID();
{
Ledger::pointer firstLedger = boost::make_shared<Ledger>(rootAddress, SYSTEM_CURRENCY_START);
assert(!!firstLedger->getAccountState(rootAddress));
firstLedger->updateHash();
firstLedger->setClosed();
firstLedger->setAccepted();
mMasterLedger.pushLedger(firstLedger);
Ledger::pointer secondLedger = boost::make_shared<Ledger>(true, boost::ref(*firstLedger));
secondLedger->setClosed();
secondLedger->setAccepted();
mMasterLedger.pushLedger(secondLedger, boost::make_shared<Ledger>(true, boost::ref(*secondLedger)), false);
assert(!!secondLedger->getAccountState(rootAddress));
mNetOps.setLastCloseTime(secondLedger->getCloseTimeNC());
}
}
void Application::loadOldLedger()
{
try
{
Ledger::pointer lastLedger = Ledger::getLastFullLedger();
if (!lastLedger)
{
std::cout << "No Ledger found?" << std::endl;
exit(-1);
}
lastLedger->setClosed();
cLog(lsINFO) << "Loading ledger " << lastLedger->getHash() << " seq:" << lastLedger->getLedgerSeq();
if (lastLedger->getAccountHash().isZero())
{
cLog(lsFATAL) << "Ledger is empty.";
assert(false);
exit(-1);
}
if (!lastLedger->walkLedger())
{
cLog(lsFATAL) << "Ledger is missing nodes.";
exit(-1);
}
if (!lastLedger->assertSane())
{
cLog(lsFATAL) << "Ledger is not sane.";
exit(-1);
}
mMasterLedger.setLedgerRangePresent(0, lastLedger->getLedgerSeq());
Ledger::pointer openLedger = boost::make_shared<Ledger>(false, boost::ref(*lastLedger));
mMasterLedger.switchLedgers(lastLedger, openLedger);
mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC());
}
catch (SHAMapMissingNode& mn)
{
cLog(lsFATAL) << "Cannot load ledger. " << mn;
exit(-1);
}
}
// vim:ts=4

View File

@@ -0,0 +1,129 @@
#ifndef __APPLICATION__
#define __APPLICATION__
#include <boost/asio.hpp>
#include "LedgerMaster.h"
#include "UniqueNodeList.h"
#include "ConnectionPool.h"
#include "ScopedLock.h"
#include "LedgerAcquire.h"
#include "TransactionMaster.h"
#include "Wallet.h"
#include "Peer.h"
#include "NetworkOPs.h"
#include "WSDoor.h"
#include "TaggedCache.h"
#include "ValidationCollection.h"
#include "Suppression.h"
#include "SNTPClient.h"
#include "../database/database.h"
#include "JobQueue.h"
#include "RPCHandler.h"
class RPCDoor;
class PeerDoor;
typedef TaggedCache< uint256, std::vector<unsigned char> > NodeCache;
class DatabaseCon
{
protected:
Database* mDatabase;
boost::recursive_mutex mLock;
public:
DatabaseCon(const std::string& name, const char *initString[], int countInit);
~DatabaseCon();
Database* getDB() { return mDatabase; }
ScopedLock getDBLock() { return ScopedLock(mLock); }
};
class Application
{
boost::asio::io_service mIOService, mAuxService;
boost::asio::io_service::work mIOWork, mAuxWork;
Wallet mWallet;
UniqueNodeList mUNL;
LedgerMaster mMasterLedger;
LedgerAcquireMaster mMasterLedgerAcquire;
TransactionMaster mMasterTransaction;
NetworkOPs mNetOps;
NodeCache mTempNodeCache;
ValidationCollection mValidations;
SuppressionTable mSuppressions;
HashedObjectStore mHashedObjectStore;
SNTPClient mSNTPClient;
JobQueue mJobQueue;
RPCHandler mRPCHandler;
DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB;
ConnectionPool mConnectionPool;
PeerDoor* mPeerDoor;
RPCDoor* mRPCDoor;
WSDoor* mWSPublicDoor;
WSDoor* mWSPrivateDoor;
uint256 mNonce256;
std::size_t mNonceST;
boost::asio::deadline_timer mSweepTimer;
std::map<std::string, Peer::pointer> mPeerMap;
boost::recursive_mutex mPeerMapLock;
void startNewLedger();
void loadOldLedger();
public:
Application();
~Application();
ConnectionPool& getConnectionPool() { return mConnectionPool; }
UniqueNodeList& getUNL() { return mUNL; }
Wallet& getWallet() { return mWallet ; }
NetworkOPs& getOPs() { return mNetOps; }
boost::asio::io_service& getIOService() { return mIOService; }
boost::asio::io_service& getAuxService() { return mAuxService; }
LedgerMaster& getMasterLedger() { return mMasterLedger; }
LedgerAcquireMaster& getMasterLedgerAcquire() { return mMasterLedgerAcquire; }
TransactionMaster& getMasterTransaction() { return mMasterTransaction; }
NodeCache& getTempNodeCache() { return mTempNodeCache; }
HashedObjectStore& getHashedObjectStore() { return mHashedObjectStore; }
ValidationCollection& getValidations() { return mValidations; }
JobQueue& getJobQueue() { return mJobQueue; }
SuppressionTable& getSuppression() { return mSuppressions; }
RPCHandler& getRPCHandler() { return mRPCHandler; }
bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); }
bool isNew(const uint256& s, uint64 p) { return mSuppressions.addSuppressionPeer(s, p); }
bool isNew(const uint256& s, uint64 p, int& f) { return mSuppressions.addSuppressionPeer(s, p, f); }
bool isNewFlag(const uint256& s, int f) { return mSuppressions.setFlag(s, f); }
bool running() { return mTxnDB != NULL; }
bool getSystemTimeOffset(int& offset) { return mSNTPClient.getOffset(offset); }
DatabaseCon* getRpcDB() { return mRpcDB; }
DatabaseCon* getTxnDB() { return mTxnDB; }
DatabaseCon* getLedgerDB() { return mLedgerDB; }
DatabaseCon* getWalletDB() { return mWalletDB; }
DatabaseCon* getHashNodeDB() { return mHashNodeDB; }
DatabaseCon* getNetNodeDB() { return mNetNodeDB; }
uint256 getNonce256() { return mNonce256; }
std::size_t getNonceST() { return mNonceST; }
void run();
void stop();
void sweep();
};
extern Application* theApp;
#endif
// vim:ts=4

View File

@@ -0,0 +1,107 @@
#include "BitcoinUtil.h"
#include <cstdarg>
#include <openssl/rand.h>
#include <ctime>
#if defined(WIN32) || defined(WIN64)
#include <windows.h>
#else
#include <sys/time.h>
#endif
using namespace std;
std::string gFormatStr("v1");
std::string FormatFullVersion()
{
return(gFormatStr);
}
string strprintf(const char* format, ...)
{
char buffer[50000];
char* p = buffer;
int limit = sizeof(buffer);
int ret;
loop
{
va_list arg_ptr;
va_start(arg_ptr, format);
ret = _vsnprintf(p, limit, format, arg_ptr);
va_end(arg_ptr);
if (ret >= 0 && ret < limit)
break;
if (p != buffer)
delete[] p;
limit *= 2;
p = new char[limit];
if (p == NULL)
throw std::bad_alloc();
}
string str(p, p+ret);
if (p != buffer)
delete[] p;
return str;
}
inline int64 GetPerformanceCounter()
{
int64 nCounter = 0;
#if defined(WIN32) || defined(WIN64)
QueryPerformanceCounter((LARGE_INTEGER*)&nCounter);
#else
timeval t;
gettimeofday(&t, NULL);
nCounter = t.tv_sec * 1000000 + t.tv_usec;
#endif
return nCounter;
}
void RandAddSeed()
{
// Seed with CPU performance counter
int64 nCounter = GetPerformanceCounter();
RAND_add(&nCounter, sizeof(nCounter), 1.5);
memset(&nCounter, 0, sizeof(nCounter));
}
//
// "Never go to sea with two chronometers; take one or three."
// Our three time sources are:
// - System clock
// - Median of other nodes's clocks
// - The user (asking the user to fix the system clock if the first two disagree)
//
int64 GetTime()
{
return time(NULL);
}
void RandAddSeedPerfmon()
{
RandAddSeed();
// This can take up to 2 seconds, so only do it every 10 minutes
static int64 nLastPerfmon;
if (GetTime() < nLastPerfmon + 10 * 60)
return;
nLastPerfmon = GetTime();
#ifdef WIN32
// Don't need this on Linux, OpenSSL automatically uses /dev/urandom
// Seed with the entire set of perfmon data
unsigned char pdata[250000];
memset(pdata, 0, sizeof(pdata));
unsigned long nSize = sizeof(pdata);
long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
RegCloseKey(HKEY_PERFORMANCE_DATA);
if (ret == ERROR_SUCCESS)
{
RAND_add(pdata, nSize, nSize/100.0);
memset(pdata, 0, nSize);
//printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M", GetTime()).c_str(), nSize);
}
#endif
}

View File

@@ -0,0 +1,106 @@
#ifndef __BITCOIN_UTIL__
#define __BITCOIN_UTIL__
// TODO: these things should all go somewhere
#include <string>
#include "types.h"
#include "uint256.h"
#include <openssl/ripemd.h>
#include <openssl/sha.h>
std::string strprintf(const char* format, ...);
std::string FormatFullVersion();
void RandAddSeedPerfmon();
static const unsigned int MAX_SIZE = 0x02000000;
#define loop for (;;)
#define PAIR(t1, t2) pair<t1, t2>
#if !defined(WIN32) && !defined(WIN64)
#define _vsnprintf(a,b,c,d) vsnprintf(a,b,c,d)
#endif
template<typename T1>
inline uint256 SHA256Hash(const T1 pbegin, const T1 pend)
{
static unsigned char pblank[1];
uint256 hash1;
SHA256((pbegin == pend ? pblank : (unsigned char*)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (unsigned char*)&hash1);
uint256 hash2;
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
}
template<typename T1, typename T2>
inline uint256 SHA256Hash(const T1 p1begin, const T1 p1end,
const T2 p2begin, const T2 p2end)
{
static unsigned char pblank[1];
uint256 hash1;
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
SHA256_Final((unsigned char*)&hash1, &ctx);
uint256 hash2;
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
}
template<typename T1, typename T2, typename T3>
inline uint256 SHA256Hash(const T1 p1begin, const T1 p1end,
const T2 p2begin, const T2 p2end,
const T3 p3begin, const T3 p3end)
{
static unsigned char pblank[1];
uint256 hash1;
SHA256_CTX ctx;
SHA256_Init(&ctx);
SHA256_Update(&ctx, (p1begin == p1end ? pblank : (unsigned char*)&p1begin[0]), (p1end - p1begin) * sizeof(p1begin[0]));
SHA256_Update(&ctx, (p2begin == p2end ? pblank : (unsigned char*)&p2begin[0]), (p2end - p2begin) * sizeof(p2begin[0]));
SHA256_Update(&ctx, (p3begin == p3end ? pblank : (unsigned char*)&p3begin[0]), (p3end - p3begin) * sizeof(p3begin[0]));
SHA256_Final((unsigned char*)&hash1, &ctx);
uint256 hash2;
SHA256((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
}
inline uint160 Hash160(const std::vector<unsigned char>& vch)
{
uint256 hash1;
SHA256(&vch[0], vch.size(), (unsigned char*)&hash1);
uint160 hash2;
RIPEMD160((unsigned char*)&hash1, sizeof(hash1), (unsigned char*)&hash2);
return hash2;
}
/*
#ifdef WIN32
// This is used to attempt to keep keying material out of swap
// Note that VirtualLock does not provide this as a guarantee on Windows,
// but, in practice, memory that has been VirtualLock'd almost never gets written to
// the pagefile except in rare circumstances where memory is extremely low.
#include <windows.h>
#define mlock(p, n) VirtualLock((p), (n));
#define munlock(p, n) VirtualUnlock((p), (n));
#else
#include <sys/mman.h>
#include <limits.h>
// This comes from limits.h if it's not defined there set a sane default
#ifndef PAGESIZE
#include <unistd.h>
#define PAGESIZE sysconf(_SC_PAGESIZE)
#endif
#define mlock(a,b) \
mlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\
(((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1))))
#define munlock(a,b) \
munlock(((void *)(((size_t)(a)) & (~((PAGESIZE)-1)))),\
(((((size_t)(a)) + (b) - 1) | ((PAGESIZE) - 1)) + 1) - (((size_t)(a)) & (~((PAGESIZE) - 1))))
#endif
*/
#endif

160
src/cpp/ripple/CallRPC.cpp Normal file
View File

@@ -0,0 +1,160 @@
#include <iostream>
#include <cstdlib>
#include <boost/asio.hpp>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/algorithm/string.hpp>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include "../json/value.h"
#include "../json/reader.h"
#include "CallRPC.h"
#include "RPC.h"
#include "Config.h"
#include "BitcoinUtil.h"
static inline bool isSwitchChar(char c)
{
#ifdef __WXMSW__
return c == '-' || c == '/';
#else
return c == '-';
#endif
}
std::string EncodeBase64(const std::string& s)
{ // FIXME: This performs terribly
BIO *b64, *bmem;
BUF_MEM *bptr;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, s.data(), s.size());
(void) BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
std::string result(bptr->data, bptr->length);
BIO_free_all(b64);
return result;
}
int commandLineRPC(const std::vector<std::string>& vCmd)
{
std::string strPrint;
int nRet = 0;
try
{
if (vCmd.empty()) return 1;
std::string strMethod = vCmd[0];
// Parameters default to strings
Json::Value params(Json::arrayValue);
for (int i = 1; i != vCmd.size(); i++)
params.append(vCmd[i]);
// Execute
Json::Value reply = callRPC(strMethod, params);
// Parse reply
Json::Value result = reply.get("result", Json::Value());
Json::Value error = reply.get("error", Json::Value());
if (result.isString() && (result.asString() == "unknown command"))
nRet=1;
if (!error.isNull())
{ // Error
strPrint = "error: " + error.toStyledString();
int code = error["code"].asInt();
nRet = abs(code);
}
else
{ // Result
if (result.isNull())
strPrint = "";
else if (result.isString())
strPrint = result.asString();
else
strPrint = result.toStyledString();
}
}
catch (std::exception& e)
{
strPrint = std::string("error: ") + e.what();
nRet = 87;
}
catch (...)
{
std::cout << "Exception CommandLineRPC()" << std::endl;
}
if (strPrint != "")
{
std::cout << strPrint << std::endl;
}
return nRet;
}
Json::Value callRPC(const std::string& strMethod, const Json::Value& params)
{
if (theConfig.RPC_USER.empty() && theConfig.RPC_PASSWORD.empty())
throw std::runtime_error("You must set rpcpassword=<password> in the configuration file"
"If the file does not exist, create it with owner-readable-only file permissions.");
// Connect to localhost
std::cout << "Connecting to: " << theConfig.RPC_IP << ":" << theConfig.RPC_PORT << std::endl;
boost::asio::ip::tcp::endpoint
endpoint(boost::asio::ip::address::from_string(theConfig.RPC_IP), theConfig.RPC_PORT);
boost::asio::ip::tcp::iostream stream;
stream.connect(endpoint);
if (stream.fail())
throw std::runtime_error("couldn't connect to server");
// HTTP basic authentication
std::string strUserPass64 = EncodeBase64(theConfig.RPC_USER + ":" + theConfig.RPC_PASSWORD);
std::map<std::string, std::string> mapRequestHeaders;
mapRequestHeaders["Authorization"] = std::string("Basic ") + strUserPass64;
// Send request
std::string strRequest = JSONRPCRequest(strMethod, params, Json::Value(1));
std::cout << "send request " << strMethod << " : " << strRequest << std::endl;
std::string strPost = createHTTPPost(strRequest, mapRequestHeaders);
stream << strPost << std::flush;
// std::cout << "post " << strPost << std::endl;
// Receive reply
std::map<std::string, std::string> mapHeaders;
std::string strReply;
int nStatus = ReadHTTP(stream, mapHeaders, strReply);
if (nStatus == 401)
throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
else if ((nStatus >= 400) && (nStatus != 400) && (nStatus != 404) && (nStatus != 500)) // ?
throw std::runtime_error(strprintf("server returned HTTP error %d", nStatus));
else if (strReply.empty())
throw std::runtime_error("no response from server");
// Parse reply
std::cout << "RPC reply: " << strReply << std::endl;
Json::Reader reader;
Json::Value valReply;
if (!reader.parse(strReply, valReply))
throw std::runtime_error("couldn't parse reply from server");
if (valReply.isNull())
throw std::runtime_error("expected reply to have result, error and id properties");
return valReply;
}
// vim:ts=4

7
src/cpp/ripple/CallRPC.h Normal file
View File

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

View File

@@ -0,0 +1,55 @@
#include "CanonicalTXSet.h"
bool CanonicalTXKey::operator<(const CanonicalTXKey& key) const
{
if (mAccount < key.mAccount) return true;
if (mAccount > key.mAccount) return false;
if (mSeq < key.mSeq) return true;
if (mSeq > key.mSeq) return false;
return mTXid < key.mTXid;
}
bool CanonicalTXKey::operator>(const CanonicalTXKey& key) const
{
if (mAccount > key.mAccount) return true;
if (mAccount < key.mAccount) return false;
if (mSeq > key.mSeq) return true;
if (mSeq < key.mSeq) return false;
return mTXid > key.mTXid;
}
bool CanonicalTXKey::operator<=(const CanonicalTXKey& key) const
{
if (mAccount < key.mAccount) return true;
if (mAccount > key.mAccount) return false;
if (mSeq < key.mSeq) return true;
if (mSeq > key.mSeq) return false;
return mTXid <= key.mTXid;
}
bool CanonicalTXKey::operator>=(const CanonicalTXKey& key)const
{
if (mAccount > key.mAccount) return true;
if (mAccount < key.mAccount) return false;
if (mSeq > key.mSeq) return true;
if (mSeq < key.mSeq) return false;
return mTXid >= key.mTXid;
}
void CanonicalTXSet::push_back(SerializedTransaction::ref txn)
{
uint256 effectiveAccount = mSetHash;
effectiveAccount ^= txn->getSourceAccount().getAccountID().to256();
mMap.insert(std::make_pair(CanonicalTXKey(effectiveAccount, txn->getSequence(), txn->getTransactionID()), txn));
}
CanonicalTXSet::iterator CanonicalTXSet::erase(const iterator& it)
{
iterator tmp = it;
++tmp;
mMap.erase(it);
return tmp;
}

View File

@@ -0,0 +1,59 @@
#ifndef __CANONICAL_TX_SET_
#define __CANONICAL_TX_SET_
#include <map>
#include "uint256.h"
#include "SerializedTransaction.h"
class CanonicalTXKey
{
protected:
uint256 mAccount, mTXid;
uint32 mSeq;
public:
CanonicalTXKey(const uint256& account, uint32 seq, const uint256& id)
: mAccount(account), mTXid(id), mSeq(seq) { ; }
bool operator<(const CanonicalTXKey&) const;
bool operator>(const CanonicalTXKey&) const;
bool operator<=(const CanonicalTXKey&) const;
bool operator>=(const CanonicalTXKey&) const;
bool operator==(const CanonicalTXKey& k) const { return mTXid == k.mTXid; }
bool operator!=(const CanonicalTXKey& k) const { return mTXid != k.mTXid; }
};
class CanonicalTXSet
{
public:
typedef std::map<CanonicalTXKey, SerializedTransaction::pointer>::iterator iterator;
typedef std::map<CanonicalTXKey, SerializedTransaction::pointer>::const_iterator const_iterator;
protected:
uint256 mSetHash;
std::map<CanonicalTXKey, SerializedTransaction::pointer> mMap;
public:
CanonicalTXSet(const uint256& lclHash) : mSetHash(lclHash) { ; }
void push_back(SerializedTransaction::ref txn);
void reset(const uint256& newLCL)
{
mSetHash = newLCL;
mMap.clear();
}
iterator erase(const iterator& it);
iterator begin() { return mMap.begin(); }
iterator end() { return mMap.end(); }
const_iterator begin() const { return mMap.begin(); }
const_iterator end() const { return mMap.end(); }
size_t size() const { return mMap.size(); }
bool empty() const { return mMap.empty(); }
};
#endif

304
src/cpp/ripple/Config.cpp Normal file
View File

@@ -0,0 +1,304 @@
#include "Config.h"
#include "utils.h"
#include <boost/lexical_cast.hpp>
#include <fstream>
#include <iostream>
#include <algorithm>
#define SECTION_ACCOUNT_PROBE_MAX "account_probe_max"
#define SECTION_DEBUG_LOGFILE "debug_logfile"
#define SECTION_FEE_ACCOUNT_CREATE "fee_account_create"
#define SECTION_FEE_DEFAULT "fee_default"
#define SECTION_FEE_NICKNAME_CREATE "fee_nickname_create"
#define SECTION_FEE_OFFER "fee_offer"
#define SECTION_FEE_OPERATION "fee_operation"
#define SECTION_FULL_HISTORY "full_history"
#define SECTION_IPS "ips"
#define SECTION_NETWORK_QUORUM "network_quorum"
#define SECTION_PEER_CONNECT_LOW_WATER "peer_connect_low_water"
#define SECTION_PEER_IP "peer_ip"
#define SECTION_PEER_PORT "peer_port"
#define SECTION_PEER_SCAN_INTERVAL_MIN "peer_scan_interval_min"
#define SECTION_PEER_SSL_CIPHER_LIST "peer_ssl_cipher_list"
#define SECTION_PEER_START_MAX "peer_start_max"
#define SECTION_RPC_ALLOW_REMOTE "rpc_allow_remote"
#define SECTION_RPC_IP "rpc_ip"
#define SECTION_RPC_PORT "rpc_port"
#define SECTION_SNTP "sntp_servers"
#define SECTION_UNL_DEFAULT "unl_default"
#define SECTION_VALIDATION_QUORUM "validation_quorum"
#define SECTION_VALIDATION_SEED "validation_seed"
#define SECTION_WEBSOCKET_PUBLIC_IP "websocket_public_ip"
#define SECTION_WEBSOCKET_PUBLIC_PORT "websocket_public_port"
#define SECTION_WEBSOCKET_IP "websocket_ip"
#define SECTION_WEBSOCKET_PORT "websocket_port"
#define SECTION_VALIDATORS "validators"
#define SECTION_VALIDATORS_SITE "validators_site"
// Fees are in XRP.
#define DEFAULT_FEE_DEFAULT 10
#define DEFAULT_FEE_ACCOUNT_CREATE 1000*SYSTEM_CURRENCY_PARTS
#define DEFAULT_FEE_NICKNAME_CREATE 1000
#define DEFAULT_FEE_OFFER DEFAULT_FEE_DEFAULT
#define DEFAULT_FEE_OPERATION 1
Config theConfig;
void Config::setup(const std::string& strConf)
{
boost::system::error_code ec;
//
// Determine the config and data directories.
// If the config file is found in the current working directory, use the current working directory as the config directory and
// that with "db" as the data directory.
//
if (!strConf.empty())
{
// --conf=<path> : everything is relative that file.
CONFIG_FILE = strConf;
CONFIG_DIR = CONFIG_FILE;
CONFIG_DIR.remove_filename();
DATA_DIR = CONFIG_DIR / "db";
}
else
{
CONFIG_DIR = boost::filesystem::current_path();
CONFIG_FILE = CONFIG_DIR / CONFIG_FILE_NAME;
DATA_DIR = CONFIG_DIR / "db";
if (exists(CONFIG_FILE)
// Can we figure out XDG dirs?
|| (!getenv("HOME") && (!getenv("XDG_CONFIG_HOME") || !getenv("XDG_DATA_HOME"))))
{
// Current working directory is fine, put dbs in a subdir.
nothing();
}
else
{
// Construct XDG config and data home.
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
std::string strHome = strGetEnv("HOME");
std::string strXdgConfigHome = strGetEnv("XDG_CONFIG_HOME");
std::string strXdgDataHome = strGetEnv("XDG_DATA_HOME");
if (strXdgConfigHome.empty())
{
// $XDG_CONFIG_HOME was not set, use default based on $HOME.
strXdgConfigHome = str(boost::format("%s/.config") % strHome);
}
if (strXdgDataHome.empty())
{
// $XDG_DATA_HOME was not set, use default based on $HOME.
strXdgDataHome = str(boost::format("%s/.local/share") % strHome);
}
CONFIG_DIR = str(boost::format("%s/" SYSTEM_NAME) % strXdgConfigHome);
CONFIG_FILE = CONFIG_DIR / CONFIG_FILE_NAME;
DATA_DIR = str(boost::format("%s/" SYSTEM_NAME) % strXdgDataHome);
boost::filesystem::create_directories(CONFIG_DIR, ec);
if (ec)
throw std::runtime_error(str(boost::format("Can not create %s") % CONFIG_DIR));
}
}
boost::filesystem::create_directories(DATA_DIR, ec);
if (ec)
throw std::runtime_error(str(boost::format("Can not create %s") % DATA_DIR));
// std::cerr << "CONFIG FILE: " << CONFIG_FILE << std::endl;
// std::cerr << "CONFIG DIR: " << CONFIG_DIR << std::endl;
// std::cerr << "DATA DIR: " << DATA_DIR << std::endl;
//
// Defaults
//
NETWORK_START_TIME = 1319844908;
PEER_PORT = SYSTEM_PEER_PORT;
RPC_PORT = 5001;
WEBSOCKET_PORT = SYSTEM_WEBSOCKET_PORT;
WEBSOCKET_PUBLIC_PORT = SYSTEM_WEBSOCKET_PUBLIC_PORT;
NUMBER_CONNECTIONS = 30;
// a new ledger every minute
LEDGER_SECONDS = 60;
LEDGER_CREATOR = false;
RPC_USER = "admin";
RPC_PASSWORD = "pass";
RPC_ALLOW_REMOTE = false;
PEER_SSL_CIPHER_LIST = DEFAULT_PEER_SSL_CIPHER_LIST;
PEER_SCAN_INTERVAL_MIN = DEFAULT_PEER_SCAN_INTERVAL_MIN;
PEER_START_MAX = DEFAULT_PEER_START_MAX;
PEER_CONNECT_LOW_WATER = DEFAULT_PEER_CONNECT_LOW_WATER;
TRANSACTION_FEE_BASE = 1000;
NETWORK_QUORUM = 0; // Don't need to see other nodes
VALIDATION_QUORUM = 1; // Only need one node to vouch
FEE_ACCOUNT_CREATE = DEFAULT_FEE_ACCOUNT_CREATE;
FEE_NICKNAME_CREATE = DEFAULT_FEE_NICKNAME_CREATE;
FEE_OFFER = DEFAULT_FEE_OFFER;
FEE_DEFAULT = DEFAULT_FEE_DEFAULT;
FEE_CONTRACT_OPERATION = DEFAULT_FEE_OPERATION;
FULL_HISTORY = false;
ACCOUNT_PROBE_MAX = 10;
VALIDATORS_SITE = DEFAULT_VALIDATORS_SITE;
RUN_STANDALONE = false;
START_UP = NORMAL;
load();
}
void Config::load()
{
std::cout << "Loading: " << CONFIG_FILE << std::endl;
std::ifstream ifsConfig(CONFIG_FILE.c_str(), std::ios::in);
if (!ifsConfig)
{
std::cerr << "Failed to open '" << CONFIG_FILE << "'." << std::endl;
}
else
{
std::string strConfigFile;
strConfigFile.assign((std::istreambuf_iterator<char>(ifsConfig)),
std::istreambuf_iterator<char>());
if (ifsConfig.bad())
{
std::cerr << "Failed to read '" << CONFIG_FILE << "'." << std::endl;
}
else
{
section secConfig = ParseSection(strConfigFile, true);
std::string strTemp;
// XXX Leak
section::mapped_type* smtTmp;
smtTmp = sectionEntries(secConfig, SECTION_VALIDATORS);
if (smtTmp)
{
VALIDATORS = *smtTmp;
// sectionEntriesPrint(&VALIDATORS, SECTION_VALIDATORS);
}
smtTmp = sectionEntries(secConfig, SECTION_IPS);
if (smtTmp)
{
IPS = *smtTmp;
// sectionEntriesPrint(&IPS, SECTION_IPS);
}
smtTmp = sectionEntries(secConfig, SECTION_SNTP);
if (smtTmp)
{
SNTP_SERVERS = *smtTmp;
}
(void) sectionSingleB(secConfig, SECTION_VALIDATORS_SITE, VALIDATORS_SITE);
(void) sectionSingleB(secConfig, SECTION_PEER_IP, PEER_IP);
if (sectionSingleB(secConfig, SECTION_PEER_PORT, strTemp))
PEER_PORT = boost::lexical_cast<int>(strTemp);
(void) sectionSingleB(secConfig, SECTION_RPC_IP, RPC_IP);
if (sectionSingleB(secConfig, SECTION_RPC_PORT, strTemp))
RPC_PORT = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, "ledger_creator" , strTemp))
LEDGER_CREATOR = boost::lexical_cast<bool>(strTemp);
if (sectionSingleB(secConfig, SECTION_RPC_ALLOW_REMOTE, strTemp))
RPC_ALLOW_REMOTE = boost::lexical_cast<bool>(strTemp);
(void) sectionSingleB(secConfig, SECTION_WEBSOCKET_IP, WEBSOCKET_IP);
if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PORT, strTemp))
WEBSOCKET_PORT = boost::lexical_cast<int>(strTemp);
(void) sectionSingleB(secConfig, SECTION_WEBSOCKET_PUBLIC_IP, WEBSOCKET_PUBLIC_IP);
if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PUBLIC_PORT, strTemp))
WEBSOCKET_PUBLIC_PORT = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_VALIDATION_SEED, strTemp))
{
VALIDATION_SEED.setSeedGeneric(strTemp);
if (VALIDATION_SEED.isValid())
{
VALIDATION_PUB = RippleAddress::createNodePublic(VALIDATION_SEED);
VALIDATION_PRIV = RippleAddress::createNodePrivate(VALIDATION_SEED);
}
}
(void) sectionSingleB(secConfig, SECTION_PEER_SSL_CIPHER_LIST, PEER_SSL_CIPHER_LIST);
if (sectionSingleB(secConfig, SECTION_PEER_SCAN_INTERVAL_MIN, strTemp))
// Minimum for min is 60 seconds.
PEER_SCAN_INTERVAL_MIN = std::max(60, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_PEER_START_MAX, strTemp))
PEER_START_MAX = std::max(1, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_PEER_CONNECT_LOW_WATER, strTemp))
PEER_CONNECT_LOW_WATER = std::max(1, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_NETWORK_QUORUM, strTemp))
NETWORK_QUORUM = std::max(0, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_VALIDATION_QUORUM, strTemp))
VALIDATION_QUORUM = std::max(0, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_FEE_ACCOUNT_CREATE, strTemp))
FEE_ACCOUNT_CREATE = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_FEE_NICKNAME_CREATE, strTemp))
FEE_NICKNAME_CREATE = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_FEE_OFFER, strTemp))
FEE_OFFER = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_FEE_DEFAULT, strTemp))
FEE_DEFAULT = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_FEE_OPERATION, strTemp))
FEE_CONTRACT_OPERATION = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_FULL_HISTORY, strTemp))
FULL_HISTORY = boost::lexical_cast<bool>(strTemp);
if (sectionSingleB(secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp))
ACCOUNT_PROBE_MAX = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_UNL_DEFAULT, strTemp))
UNL_DEFAULT = strTemp;
if (sectionSingleB(secConfig, SECTION_DEBUG_LOGFILE, strTemp))
DEBUG_LOGFILE = strTemp;
}
}
}
// vim:ts=4

122
src/cpp/ripple/Config.h Normal file
View File

@@ -0,0 +1,122 @@
#ifndef __CONFIG__
#define __CONFIG__
#include "types.h"
#include "RippleAddress.h"
#include "ParseSection.h"
#include "SerializedTypes.h"
#include <string>
#include <boost/filesystem.hpp>
#define SYSTEM_NAME "ripple"
#define SYSTEM_CURRENCY_CODE "XRP"
#define SYSTEM_CURRENCY_PRECISION 6
#define SYSTEM_CURRENCY_CODE_RIPPLE "XRR"
#define SYSTEM_CURRENCY_GIFT 1000ull
#define SYSTEM_CURRENCY_USERS 100000000ull
#define SYSTEM_CURRENCY_PARTS 1000000ull // 10^SYSTEM_CURRENCY_PRECISION
#define SYSTEM_CURRENCY_START (SYSTEM_CURRENCY_GIFT*SYSTEM_CURRENCY_USERS*SYSTEM_CURRENCY_PARTS)
#define CONFIG_FILE_NAME SYSTEM_NAME "d.cfg" // rippled.cfg
#define DEFAULT_VALIDATORS_SITE "redstem.com"
#define VALIDATORS_FILE_NAME "validators.txt"
const int SYSTEM_PEER_PORT = 6561;
const int SYSTEM_WEBSOCKET_PORT = 6562;
const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away.
// Allow anonymous DH.
#define DEFAULT_PEER_SSL_CIPHER_LIST "ALL:!LOW:!EXP:!MD5:@STRENGTH"
// Normal, recommend 1 hour.
// #define DEFAULT_PEER_SCAN_INTERVAL_MIN (60*60)
// Testing, recommend 1 minute.
#define DEFAULT_PEER_SCAN_INTERVAL_MIN (60)
// Maximum number of peers to try to connect to as client at once.
#define DEFAULT_PEER_START_MAX 5
// Might connect with fewer for testing.
#define DEFAULT_PEER_CONNECT_LOW_WATER 4
class Config
{
public:
// Configuration parameters
boost::filesystem::path CONFIG_FILE;
boost::filesystem::path CONFIG_DIR;
boost::filesystem::path DATA_DIR;
boost::filesystem::path DEBUG_LOGFILE;
boost::filesystem::path UNL_DEFAULT;
std::string VALIDATORS_SITE; // Where to find validators.txt on the Internet.
std::vector<std::string> VALIDATORS; // Validators from rippled.cfg.
std::vector<std::string> IPS; // Peer IPs from rippled.cfg.
std::vector<std::string> SNTP_SERVERS; // SNTP servers from rippled.cfg.
enum StartUpType { FRESH, NORMAL, LOAD, NETWORK };
StartUpType START_UP;
// Network parameters
int NETWORK_START_TIME; // The Unix time we start ledger 0.
int TRANSACTION_FEE_BASE;
int LEDGER_SECONDS;
int LEDGER_PROPOSAL_DELAY_SECONDS;
int LEDGER_AVALANCHE_SECONDS;
bool LEDGER_CREATOR; // should be false unless we are starting a new ledger
bool RUN_STANDALONE;
// Note: The following parameters do not relate to the UNL or trust at all
unsigned int NETWORK_QUORUM; // Minimum number of nodes to consider the network present
int VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative
// Peer networking parameters
std::string PEER_IP;
int PEER_PORT;
int NUMBER_CONNECTIONS;
std::string PEER_SSL_CIPHER_LIST;
int PEER_SCAN_INTERVAL_MIN;
int PEER_START_MAX;
unsigned int PEER_CONNECT_LOW_WATER;
// Websocket networking parameters
std::string WEBSOCKET_PUBLIC_IP; // XXX Going away. Merge with the inbound peer connction.
int WEBSOCKET_PUBLIC_PORT;
std::string WEBSOCKET_IP;
int WEBSOCKET_PORT;
// RPC parameters
std::string RPC_IP;
int RPC_PORT;
std::string RPC_USER;
std::string RPC_PASSWORD;
bool RPC_ALLOW_REMOTE;
// Validation
RippleAddress VALIDATION_SEED, VALIDATION_PUB, VALIDATION_PRIV;
// Fees
uint64 FEE_DEFAULT; // Default fee.
uint64 FEE_ACCOUNT_CREATE; // Fee to create an account.
uint64 FEE_NICKNAME_CREATE; // Fee to create a nickname.
uint64 FEE_OFFER; // Rate per day.
int FEE_CONTRACT_OPERATION; // fee for each contract operation
// Node storage configuration
bool FULL_HISTORY;
// Client behavior
int ACCOUNT_PROBE_MAX; // How far to scan for accounts.
void setup(const std::string& strConf);
void load();
};
extern Config theConfig;
#endif
// vim:ts=4

View File

@@ -0,0 +1,764 @@
#include "ConnectionPool.h"
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <algorithm>
#include "Config.h"
#include "Peer.h"
#include "Application.h"
#include "utils.h"
#include "Log.h"
SETUP_LOG();
// How often to enforce policies.
#define POLICY_INTERVAL_SECONDS 5
void splitIpPort(const std::string& strIpPort, std::string& strIp, int& iPort)
{
std::vector<std::string> vIpPort;
boost::split(vIpPort, strIpPort, boost::is_any_of(" "));
strIp = vIpPort[0];
iPort = boost::lexical_cast<int>(vIpPort[1]);
}
ConnectionPool::ConnectionPool(boost::asio::io_service& io_service) :
mLastPeer(0),
mCtx(boost::asio::ssl::context::sslv23),
mScanTimer(io_service),
mPolicyTimer(io_service)
{
mCtx.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::single_dh_use);
if (1 != SSL_CTX_set_cipher_list(mCtx.native_handle(), theConfig.PEER_SSL_CIPHER_LIST.c_str()))
std::runtime_error("Error setting cipher list (no valid ciphers).");
}
void ConnectionPool::start()
{
if (theConfig.RUN_STANDALONE)
return;
// Start running policy.
policyEnforce();
// Start scanning.
scanRefresh();
}
bool ConnectionPool::getTopNAddrs(int n,std::vector<std::string>& addrs)
{
// XXX Filter out other local addresses (like ipv6)
Database* db = theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock());
SQL_FOREACH(db, str(boost::format("SELECT IpPort FROM PeerIps LIMIT %d") % n) )
{
std::string str;
db->getStr(0,str);
addrs.push_back(str);
}
return true;
}
bool ConnectionPool::savePeer(const std::string& strIp, int iPort, char code)
{
bool bNew = false;
Database* db = theApp->getWalletDB()->getDB();
std::string ipPort = sqlEscape(str(boost::format("%s %d") % strIp % iPort));
ScopedLock sl(theApp->getWalletDB()->getDBLock());
std::string sql = str(boost::format("SELECT COUNT(*) FROM PeerIps WHERE IpPort=%s;") % ipPort);
if (db->executeSQL(sql) && db->startIterRows())
{
if (!db->getInt(0))
{
db->executeSQL(str(boost::format("INSERT INTO PeerIps (IpPort,Score,Source) values (%s,0,'%c');") % ipPort % code));
bNew = true;
}
else
{
// We already had this peer.
// We will eventually verify its address if it is possible.
// YYY If it is vsInbound, then we might make verification immediate so we can connect back sooner if the connection
// is lost.
nothing();
}
}
else
{
std::cout << "Error saving Peer" << std::endl;
}
if (bNew)
scanRefresh();
return bNew;
}
Peer::pointer ConnectionPool::getPeerById(const uint64& id)
{
boost::mutex::scoped_lock sl(mPeerLock);
const boost::unordered_map<uint64, Peer::pointer>::iterator& it = mPeerIdMap.find(id);
if (it == mPeerIdMap.end())
return Peer::pointer();
return it->second;
}
bool ConnectionPool::hasPeer(const uint64& id)
{
boost::mutex::scoped_lock sl(mPeerLock);
return mPeerIdMap.find(id) != mPeerIdMap.end();
}
// An available peer is one we had no trouble connect to last time and that we are not currently knowingly connected or connecting
// too.
//
// <-- true, if a peer is available to connect to
bool ConnectionPool::peerAvailable(std::string& strIp, int& iPort)
{
Database* db = theApp->getWalletDB()->getDB();
std::vector<std::string> vstrIpPort;
// Convert mIpMap (list of open connections) to a vector of "<ip> <port>".
{
boost::mutex::scoped_lock sl(mPeerLock);
vstrIpPort.reserve(mIpMap.size());
BOOST_FOREACH(pipPeer ipPeer, mIpMap)
{
const std::string& strIp = ipPeer.first.first;
int iPort = ipPeer.first.second;
vstrIpPort.push_back(sqlEscape(str(boost::format("%s %d") % strIp % iPort)));
}
}
// Get the first IpPort entry which is not in vector and which is not scheduled for scanning.
std::string strIpPort;
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
if (db->executeSQL(str(boost::format("SELECT IpPort FROM PeerIps WHERE ScanNext IS NULL AND IpPort NOT IN (%s) LIMIT 1;")
% strJoin(vstrIpPort.begin(), vstrIpPort.end(), ",")))
&& db->startIterRows())
{
strIpPort = db->getStrBinary("IpPort");
}
}
bool bAvailable = !strIpPort.empty();
if (bAvailable)
splitIpPort(strIpPort, strIp, iPort);
return bAvailable;
}
// Make sure we have at least low water connections.
void ConnectionPool::policyLowWater()
{
std::string strIp;
int iPort;
// Find an entry to connect to.
if (mConnectedMap.size() > theConfig.PEER_CONNECT_LOW_WATER)
{
// Above low water mark, don't need more connections.
cLog(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER;
nothing();
}
#if 0
else if (miConnectStarting == theConfig.PEER_START_MAX)
{
// Too many connections starting to start another.
nothing();
}
#endif
else if (!peerAvailable(strIp, iPort))
{
// No more connections available to start.
cLog(lsTRACE) << "Pool: Low water: no peers available.";
// XXX Might ask peers for more ips.
nothing();
}
else
{
// Try to start connection.
cLog(lsTRACE) << "Pool: Low water: start connection.";
if (!peerConnect(strIp, iPort))
{
cLog(lsINFO) << "Pool: Low water: already connected.";
}
// Check if we need more.
policyLowWater();
}
}
void ConnectionPool::policyEnforce()
{
// Cancel any in progress timer.
(void) mPolicyTimer.cancel();
// Enforce policies.
policyLowWater();
// Schedule next enforcement.
mPolicyTimer.expires_at(boost::posix_time::second_clock::universal_time()+boost::posix_time::seconds(POLICY_INTERVAL_SECONDS));
mPolicyTimer.async_wait(boost::bind(&ConnectionPool::policyHandler, this, _1));
}
void ConnectionPool::policyHandler(const boost::system::error_code& ecResult)
{
if (ecResult == boost::asio::error::operation_aborted)
{
nothing();
}
else if (!ecResult)
{
policyEnforce();
}
else
{
throw std::runtime_error("Internal error: unexpected deadline error.");
}
}
// YYY: Should probably do this in the background.
// YYY: Might end up sending to disconnected peer?
int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg)
{
int sentTo = 0;
boost::mutex::scoped_lock sl(mPeerLock);
BOOST_FOREACH(naPeer pair, mConnectedMap)
{
Peer::ref peer = pair.second;
if (!peer)
std::cerr << "CP::RM null peer in list" << std::endl;
else if ((!fromPeer || !(peer.get() == fromPeer)) && peer->isConnected())
{
++sentTo;
peer->sendPacket(msg);
}
}
return sentTo;
}
void ConnectionPool::relayMessageBut(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
{ // Relay message to all but the specified peers
boost::mutex::scoped_lock sl(mPeerLock);
BOOST_FOREACH(naPeer pair, mConnectedMap)
{
Peer::ref peer = pair.second;
if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) == 0))
peer->sendPacket(msg);
}
}
void ConnectionPool::relayMessageTo(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
{ // Relay message to the specified peers
boost::mutex::scoped_lock sl(mPeerLock);
BOOST_FOREACH(const uint64& peerID, fromPeers)
{
const boost::unordered_map<uint64, Peer::pointer>::iterator& it = mPeerIdMap.find(peerID);
if ((it != mPeerIdMap.end()) && it->second->isConnected())
it->second->sendPacket(msg);
}
}
// Schedule a connection via scanning.
//
// Add or modify into PeerIps as a manual entry for immediate scanning.
// Requires sane IP and port.
void ConnectionPool::connectTo(const std::string& strIp, int iPort)
{
if (theConfig.RUN_STANDALONE)
return;
{
Database* db = theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(str(boost::format("REPLACE INTO PeerIps (IpPort,Score,Source,ScanNext) values (%s,%d,'%c',0);")
% sqlEscape(str(boost::format("%s %d") % strIp % iPort))
% theApp->getUNL().iSourceScore(UniqueNodeList::vsManual)
% char(UniqueNodeList::vsManual)));
}
scanRefresh();
}
// Start a connection, if not already known connected or connecting.
//
// <-- true, if already connected.
Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort)
{
ipPort pipPeer = make_pair(strIp, iPort);
Peer::pointer ppResult;
boost::unordered_map<ipPort, Peer::pointer>::iterator it;
{
boost::mutex::scoped_lock sl(mPeerLock);
if ((it = mIpMap.find(pipPeer)) == mIpMap.end())
{
Peer::pointer ppNew(Peer::create(theApp->getIOService(), mCtx, ++mLastPeer));
// Did not find it. Not already connecting or connected.
ppNew->connect(strIp, iPort);
mIpMap[pipPeer] = ppNew;
ppResult = ppNew;
// ++miConnectStarting;
}
else
{
// Found it. Already connected.
nothing();
}
}
if (ppResult)
{
//cLog(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort;
}
else
{
//cLog(lsINFO) << "Pool: Already connected: " << strIp << " " << iPort;
}
return ppResult;
}
// Returns information on verified peers.
Json::Value ConnectionPool::getPeersJson()
{
Json::Value ret(Json::arrayValue);
std::vector<Peer::pointer> vppPeers = getPeerVector();
BOOST_FOREACH(Peer::pointer peer, vppPeers)
{
ret.append(peer->getJson());
}
return ret;
}
int ConnectionPool::getPeerCount()
{
boost::mutex::scoped_lock sl(mPeerLock);
return mConnectedMap.size();
}
std::vector<Peer::pointer> ConnectionPool::getPeerVector()
{
std::vector<Peer::pointer> ret;
boost::mutex::scoped_lock sl(mPeerLock);
ret.reserve(mConnectedMap.size());
BOOST_FOREACH(naPeer pair, mConnectedMap)
{
assert(!!pair.second);
ret.push_back(pair.second);
}
return ret;
}
uint64 ConnectionPool::assignPeerId()
{
boost::mutex::scoped_lock sl(mPeerLock);
return ++mLastPeer;
}
// Now know peer's node public key. Determine if we want to stay connected.
// <-- bNew: false = redundant
bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer,
const std::string& strIP, int iPort)
{
bool bNew = false;
assert(!!peer);
if (naPeer == theApp->getWallet().getNodePublic())
{
cLog(lsINFO) << "Pool: Connected: self: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
}
else
{
boost::mutex::scoped_lock sl(mPeerLock);
const boost::unordered_map<RippleAddress, Peer::pointer>::iterator& itCm = mConnectedMap.find(naPeer);
if (itCm == mConnectedMap.end())
{
// New connection.
//cLog(lsINFO) << "Pool: Connected: new: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort;
mConnectedMap[naPeer] = peer;
bNew = true;
assert(peer->getPeerId() != 0);
mPeerIdMap.insert(std::make_pair(peer->getPeerId(), peer));
}
// Found in map, already connected.
else if (!strIP.empty())
{
// Was an outbound connection, we know IP and port.
// Note in previous connection how to reconnect.
if (itCm->second->getIP().empty())
{
// Old peer did not know it's IP.
//cLog(lsINFO) << "Pool: Connected: redundant: outbound: " << ADDRESS_SHARED(peer) << " discovered: " << ADDRESS_SHARED(itCm->second) << ": " << strIP << " " << iPort;
itCm->second->setIpPort(strIP, iPort);
// Add old connection to identified connection list.
mIpMap[make_pair(strIP, iPort)] = itCm->second;
}
else
{
// Old peer knew its IP. Do nothing.
//cLog(lsINFO) << "Pool: Connected: redundant: outbound: rediscovered: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort;
nothing();
}
}
else
{
//cLog(lsINFO) << "Pool: Connected: redundant: inbound: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort;
nothing();
}
}
return bNew;
}
// We maintain a map of public key to peer for connected and verified peers. Maintain it.
void ConnectionPool::peerDisconnected(Peer::ref peer, const RippleAddress& naPeer)
{
boost::mutex::scoped_lock sl(mPeerLock);
if (naPeer.isValid())
{
const boost::unordered_map<RippleAddress, Peer::pointer>::iterator& itCm = mConnectedMap.find(naPeer);
if (itCm == mConnectedMap.end())
{
// Did not find it. Not already connecting or connected.
cLog(lsWARNING) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent.";
// XXX Maybe bad error, considering we have racing connections, may not so bad.
}
else if (itCm->second != peer)
{
cLog(lsWARNING) << "Pool: disconected: non canonical entry";
nothing();
}
else
{
// Found it. Delete it.
mConnectedMap.erase(itCm);
//cLog(lsINFO) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort();
}
}
else
{
//cLog(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort();
}
assert(peer->getPeerId() != 0);
mPeerIdMap.erase(peer->getPeerId());
}
// Schedule for immediate scanning, if not already scheduled.
//
// <-- true, scanRefresh needed.
bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort)
{
std::string strIpPort = str(boost::format("%s %d") % strIp % iPort);
bool bScanDirty = false;
ScopedLock sl(theApp->getWalletDB()->getDBLock());
Database* db = theApp->getWalletDB()->getDB();
if (db->executeSQL(str(boost::format("SELECT ScanNext FROM PeerIps WHERE IpPort=%s;")
% sqlEscape(strIpPort)))
&& db->startIterRows())
{
if (db->getNull("ScanNext"))
{
// Non-scanning connection terminated. Schedule for scanning.
int iInterval = theConfig.PEER_SCAN_INTERVAL_MIN;
boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time();
boost::posix_time::ptime tpNext = tpNow + boost::posix_time::seconds(iInterval);
//cLog(lsINFO) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)")
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
% iToSeconds(tpNext)
% iInterval
% db->escape(strIpPort)));
bScanDirty = true;
}
else
{
// Scan connection terminated, already scheduled for retry.
// boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time();
// boost::posix_time::ptime tpNext = ptFromSeconds(db->getInt("ScanNext"));
//cLog(lsINFO) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)")
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
}
}
else
{
//cLog(lsWARNING) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort;
}
return bScanDirty;
}
// --> strIp: not empty
void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iPort)
{
ipPort ipPeer = make_pair(strIp, iPort);
bool bScanRefresh = false;
// If the connection was our scan, we are no longer scanning.
if (mScanning && mScanning == peer)
{
//cLog(lsINFO) << "Pool: Scan: scan fail: " << strIp << " " << iPort;
mScanning.reset(); // No longer scanning.
bScanRefresh = true; // Look for more to scan.
}
// Determine if closed peer was redundant.
bool bRedundant = true;
{
boost::mutex::scoped_lock sl(mPeerLock);
const boost::unordered_map<ipPort, Peer::pointer>::iterator& itIp = mIpMap.find(ipPeer);
if (itIp == mIpMap.end())
{
// Did not find it. Not already connecting or connected.
cLog(lsWARNING) << "Pool: Closed: UNEXPECTED: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
// XXX Internal error.
}
else if (mIpMap[ipPeer] == peer)
{
// We were the identified connection.
//cLog(lsINFO) << "Pool: Closed: identified: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
// Delete our entry.
mIpMap.erase(itIp);
bRedundant = false;
}
else
{
// Found it. But, we were redundant.
//cLog(lsINFO) << "Pool: Closed: redundant: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort;
}
}
if (!bRedundant)
{
// If closed was not redundant schedule if not already scheduled.
bScanRefresh = peerScanSet(ipPeer.first, ipPeer.second) || bScanRefresh;
}
if (bScanRefresh)
scanRefresh();
}
void ConnectionPool::peerVerified(Peer::ref peer)
{
if (mScanning && mScanning == peer)
{
// Scan completed successfully.
std::string strIp = peer->getIP();
int iPort = peer->getPort();
std::string strIpPort = str(boost::format("%s %d") % strIp % iPort);
//cLog(lsINFO) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % ADDRESS_SHARED(peer) % strIp % iPort);
if (peer->getNodePublic() == theApp->getWallet().getNodePublic())
{
// Talking to ourself. We will just back off. This lets us maybe advertise our outside address.
nothing(); // Do nothing, leave scheduled scanning.
}
else
{
// Talking with a different peer.
ScopedLock sl(theApp->getWalletDB()->getDBLock());
Database *db=theApp->getWalletDB()->getDB();
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=NULL,ScanInterval=0 WHERE IpPort=%s;")
% db->escape(strIpPort)));
// XXX Check error.
}
mScanning.reset();
scanRefresh(); // Continue scanning.
}
}
void ConnectionPool::scanHandler(const boost::system::error_code& ecResult)
{
if (ecResult == boost::asio::error::operation_aborted)
{
nothing();
}
else if (!ecResult)
{
scanRefresh();
}
else
{
throw std::runtime_error("Internal error: unexpected deadline error.");
}
}
// Scan ips as per db entries.
void ConnectionPool::scanRefresh()
{
if (mScanning)
{
// Currently scanning, will scan again after completion.
cLog(lsTRACE) << "Pool: Scan: already scanning";
nothing();
}
else
{
// Discover if there are entries that need scanning.
boost::posix_time::ptime tpNext;
boost::posix_time::ptime tpNow;
std::string strIpPort;
int iInterval;
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
Database* db = theApp->getWalletDB()->getDB();
if (db->executeSQL("SELECT * FROM PeerIps INDEXED BY PeerScanIndex WHERE ScanNext NOT NULL ORDER BY ScanNext LIMIT 1;")
&& db->startIterRows())
{
// Have an entry to scan.
int iNext = db->getInt("ScanNext");
tpNext = ptFromSeconds(iNext);
tpNow = boost::posix_time::second_clock::universal_time();
db->getStr("IpPort", strIpPort);
iInterval = db->getInt("ScanInterval");
}
else
{
// No entries to scan.
tpNow = boost::posix_time::ptime(boost::posix_time::not_a_date_time);
}
}
if (tpNow.is_not_a_date_time())
{
//cLog(lsINFO) << "Pool: Scan: stop.";
(void) mScanTimer.cancel();
}
else if (tpNext <= tpNow)
{
// Scan it.
splitIpPort(strIpPort, mScanIp, mScanPort);
(void) mScanTimer.cancel();
iInterval = std::max(iInterval, theConfig.PEER_SCAN_INTERVAL_MIN);
tpNext = tpNow + boost::posix_time::seconds(iInterval);
//cLog(lsINFO) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)")
// % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds());
iInterval *= 2;
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
Database *db=theApp->getWalletDB()->getDB();
db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;")
% iToSeconds(tpNext)
% iInterval
% db->escape(strIpPort)));
// XXX Check error.
}
mScanning = peerConnect(mScanIp, mScanPort);
if (!mScanning)
{
// Already connected. Try again.
scanRefresh();
}
}
else
{
//cLog(lsINFO) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)")
// % strIpPort % tpNext % (tpNext-tpNow).total_seconds());
mScanTimer.expires_at(tpNext);
mScanTimer.async_wait(boost::bind(&ConnectionPool::scanHandler, this, _1));
}
}
}
#if 0
bool ConnectionPool::isMessageKnown(PackedMessage::pointer msg)
{
for(unsigned int n=0; n<mBroadcastMessages.size(); n++)
{
if(msg==mBroadcastMessages[n].first) return(false);
}
return(false);
}
#endif
// vim:ts=4

View File

@@ -0,0 +1,120 @@
#ifndef __CONNECTION_POOL__
#define __CONNECTION_POOL__
#include <set>
#include <boost/asio/ssl.hpp>
#include <boost/thread/mutex.hpp>
#include "Peer.h"
#include "PackedMessage.h"
#include "types.h"
//
// Access to the Ripple network.
//
class ConnectionPool
{
private:
boost::mutex mPeerLock;
uint64 mLastPeer;
typedef std::pair<RippleAddress, Peer::pointer> naPeer;
typedef std::pair<ipPort, Peer::pointer> pipPeer;
// Peers we are connecting with and non-thin peers we are connected to.
// Only peers we know the connection ip for are listed.
// We know the ip and port for:
// - All outbound connections
// - Some inbound connections (which we figured out).
boost::unordered_map<ipPort, Peer::pointer> mIpMap;
// Non-thin peers which we are connected to.
// Peers we have the public key for.
boost::unordered_map<RippleAddress, Peer::pointer> mConnectedMap;
// Connections with have a 64-bit identifier
boost::unordered_map<uint64, Peer::pointer> mPeerIdMap;
boost::asio::ssl::context mCtx;
Peer::pointer mScanning;
boost::asio::deadline_timer mScanTimer;
std::string mScanIp;
int mScanPort;
void scanHandler(const boost::system::error_code& ecResult);
boost::asio::deadline_timer mPolicyTimer;
void policyHandler(const boost::system::error_code& ecResult);
// Peers we are establishing a connection with as a client.
// int miConnectStarting;
bool peerAvailable(std::string& strIp, int& iPort);
bool peerScanSet(const std::string& strIp, int iPort);
Peer::pointer peerConnect(const std::string& strIp, int iPort);
public:
ConnectionPool(boost::asio::io_service& io_service);
// Begin enforcing connection policy.
void start();
// Send message to network.
int relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg);
void relayMessageTo(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg);
void relayMessageBut(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg);
// Manual connection request.
// Queue for immediate scanning.
void connectTo(const std::string& strIp, int iPort);
//
// Peer connectivity notification.
//
bool getTopNAddrs(int n,std::vector<std::string>& addrs);
bool savePeer(const std::string& strIp, int iPort, char code);
// We know peers node public key.
// <-- bool: false=reject
bool peerConnected(Peer::ref peer, const RippleAddress& naPeer, const std::string& strIP, int iPort);
// No longer connected.
void peerDisconnected(Peer::ref peer, const RippleAddress& naPeer);
// As client accepted.
void peerVerified(Peer::ref peer);
// As client failed connect and be accepted.
void peerClosed(Peer::ref peer, const std::string& strIp, int iPort);
int getPeerCount();
Json::Value getPeersJson();
std::vector<Peer::pointer> getPeerVector();
// Peer 64-bit ID function
uint64 assignPeerId();
Peer::pointer getPeerById(const uint64& id);
bool hasPeer(const uint64& id);
//
// Scanning
//
void scanRefresh();
//
// Connection policy
//
void policyLowWater();
void policyEnforce();
};
extern void splitIpPort(const std::string& strIpPort, std::string& strIp, int& iPort);
#endif
// vim:ts=4

View File

@@ -0,0 +1,35 @@
#include "Contract.h"
#include "Interpreter.h"
using namespace Script;
/*
JED: V III
*/
Contract::Contract()
{
}
void Contract::executeCreate()
{
}
void Contract::executeRemove()
{
}
void Contract::executeFund()
{
}
void Contract::executeAccept()
{
//std::vector<char> code;
//Interpreter interpreter;
//interpreter.interpret(this,code);
}

30
src/cpp/ripple/Contract.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef __CONTRACT__
#define __CONTRACT__
#include "SerializedLedger.h"
#include <boost/shared_ptr.hpp>
#include "ScriptData.h"
/*
Encapsulates the SLE for a Contract
*/
class Contract
{
public:
Contract();
uint160& getIssuer();
uint160& getOwner();
STAmount& getRippleEscrow();
uint32 getEscrow();
uint32 getBond();
Script::Data getData(int index);
void executeCreate();
void executeRemove();
void executeFund();
void executeAccept();
};
#endif

288
src/cpp/ripple/DBInit.cpp Normal file
View File

@@ -0,0 +1,288 @@
#include "utils.h"
#include <string>
// Transaction database holds transactions and public keys
const char *TxnDBInit[] = {
"BEGIN TRANSACTION;",
"CREATE TABLE Transactions ( \
TransID CHARACTER(64) PRIMARY KEY, \
TransType CHARACTER(24), \
FromAcct CHARACTER(35), \
FromSeq BIGINT UNSIGNED, \
LedgerSeq BIGINT UNSIGNED, \
Status CHARACTER(1), \
RawTxn BLOB \
TxnMeta BLOB \
);",
"CREATE TABLE PubKeys ( \
ID CHARACTER(35) PRIMARY KEY, \
PubKey BLOB \
);",
"CREATE TABLE AccountTransactions ( \
TransID CHARACTER(64), \
Account CHARACTER(64), \
LedgerSeq BIGINT UNSIGNED \
);",
"CREATE INDEX AcctTxindex ON \
AccountTransactions(Account, LedgerSeq, TransID);",
"CREATE INDEX AcctLgrIndex ON \
AccountTransactions(LedgerSeq, Account, TransID);",
"END TRANSACTION;"
};
int TxnDBCount = NUMBER(TxnDBInit);
// Ledger database holds ledgers and ledger confirmations
const char *LedgerDBInit[] = {
"BEGIN TRANSACTION;",
"CREATE TABLE Ledgers ( \
LedgerHash CHARACTER(64) PRIMARY KEY, \
LedgerSeq BIGINT UNSIGNED, \
PrevHash CHARACTER(64), \
TotalCoins BIGINT UNSIGNED, \
ClosingTime BIGINT UNSIGNED, \
PrevClosingTime BIGINT UNSIGNED, \
CloseTimeRes BIGINT UNSIGNED, \
CloseFlags BIGINT UNSIGNED, \
AccountSetHash CHARACTER(64), \
TransSetHash CHARACTER(64) \
);",
"CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);",
"CREATE TABLE LedgerValidations ( \
LedgerHash CHARACTER(64), \
NodePubKey CHARACTER(56), \
Flags BIGINT UNSIGNED, \
SignTime BIGINT UNSIGNED, \
Signature BLOB \
);",
"CREATE INDEX ValidationByHash ON \
LedgerValidations(LedgerHash);",
"END TRANSACTION;"
};
int LedgerDBCount = NUMBER(LedgerDBInit);
// RPC database holds persistent data for RPC clients.
const char *RpcDBInit[] = {
// Local persistence of the RPC client
"CREATE TABLE RPCData ( \
Key TEXT PRIMARY Key, \
Value TEXT \
);",
};
int RpcDBCount = NUMBER(RpcDBInit);
// Wallet database holds local accounts and trusted nodes
const char *WalletDBInit[] = {
// Node identity must be persisted for CAS routing and responsibilities.
"BEGIN TRANSACTION;",
"CREATE TABLE NodeIdentity ( \
PublicKey CHARACTER(53), \
PrivateKey CHARACTER(52), \
Dh512 TEXT, \
Dh1024 TEXT \
);",
// Miscellaneous persistent information
// Integer: 1 : Used to simplify SQL.
// ScoreUpdated: when scores was last updated.
// FetchUpdated: when last fetch succeeded.
"CREATE TABLE Misc ( \
Magic INTEGER UNIQUE NOT NULL, \
ScoreUpdated DATETIME, \
FetchUpdated DATETIME \
);",
// Scoring and other information for domains.
//
// Domain:
// Domain source for https.
// PublicKey:
// Set if ever succeeded.
// XXX Use NULL in place of ""
// Source:
// 'M' = Manually added. : 1500
// 'V' = validators.txt : 1000
// 'W' = Web browsing. : 200
// 'R' = Referral : 0
// Next:
// Time of next fetch attempt.
// Scan:
// Time of last fetch attempt.
// Fetch:
// Time of last successful fetch.
// Sha256:
// Checksum of last fetch.
// Comment:
// User supplied comment.
// Table of Domains user has asked to trust.
"CREATE TABLE SeedDomains ( \
Domain TEXT PRIMARY KEY NOT NULL, \
PublicKey CHARACTER(53), \
Source CHARACTER(1) NOT NULL, \
Next DATETIME, \
Scan DATETIME, \
Fetch DATETIME, \
Sha256 CHARACTER[64], \
Comment TEXT \
);",
// Allow us to easily find the next SeedDomain to fetch.
"CREATE INDEX SeedDomainNext ON SeedDomains (Next);",
// Table of PublicKeys user has asked to trust.
// Fetches are made to the CAS. This gets the ripple.txt so even validators without a web server can publish a ripple.txt.
// Source:
// 'M' = Manually added. : 1500
// 'V' = validators.txt : 1000
// 'W' = Web browsing. : 200
// 'R' = Referral : 0
// Next:
// Time of next fetch attempt.
// Scan:
// Time of last fetch attempt.
// Fetch:
// Time of last successful fetch.
// Sha256:
// Checksum of last fetch.
// Comment:
// User supplied comment.
"CREATE TABLE SeedNodes ( \
PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \
Source CHARACTER(1) NOT NULL, \
Next DATETIME, \
Scan DATETIME, \
Fetch DATETIME, \
Sha256 CHARACTER[64], \
Comment TEXT \
);",
// Allow us to easily find the next SeedNode to fetch.
"CREATE INDEX SeedNodeNext ON SeedNodes (Next);",
// Nodes we trust to not grossly collude against us. Derived from SeedDomains, SeedNodes, and ValidatorReferrals.
//
// Score:
// Computed trust score. Higher is better.
// Seen:
// Last validation received.
"CREATE TABLE TrustedNodes ( \
PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \
Score INTEGER DEFAULT 0 NOT NULL, \
Seen DATETIME, \
Comment TEXT \
);",
// List of referrals.
// - There may be multiple sources for a Validator. The last source is used.
// Validator:
// Public key of referrer.
// Entry:
// Entry index in [validators] table.
// Referral:
// This is the form provided by the ripple.txt:
// - Public key for CAS based referral.
// - Domain for domain based referral.
// XXX Do garbage collection when validators have no references.
"CREATE TABLE ValidatorReferrals ( \
Validator CHARACTER(53) NOT NULL, \
Entry INTEGER NOT NULL, \
Referral TEXT NOT NULL, \
PRIMARY KEY (Validator,Entry) \
);",
// List of referrals from ripple.txt files.
// Validator:
// Public key of referree.
// Entry:
// Entry index in [validators] table.
// IP:
// IP of referred.
// Port:
// -1 = Default
// XXX Do garbage collection when ips have no references.
"CREATE TABLE IpReferrals ( \
Validator CHARACTER(53) NOT NULL, \
Entry INTEGER NOT NULL, \
IP TEXT NOT NULL, \
Port INTEGER NOT NULL DEFAULT -1, \
PRIMARY KEY (Validator,Entry) \
);",
// Table of IPs to contact the network.
// IP:
// IP address to contact.
// Port:
// Port to contact.
// -1 = Default
// Score:
// Computed trust score. Higher is better.
// Source:
// 'V' = Validation file
// 'M' = Manually added.
// 'I' = Inbound connection.
// 'T' = Told by other peer
// 'O' = Other.
// ScanNext:
// When to next scan. Null=not scanning.
// ScanInterval:
// Delay between scans.
"CREATE TABLE PeerIps ( \
IpPort TEXT NOT NULL PRIMARY KEY, \
Score INTEGER NOT NULL DEFAULT 0, \
Source CHARACTER(1) NOT NULL, \
ScanNext DATETIME DEFAULT 0, \
ScanInterval INTEGER NOT NULL DEFAULT 0 \
);",
"CREATE INDEX PeerScanIndex ON \
PeerIps(ScanNext);",
"END TRANSACTION;"
};
int WalletDBCount = NUMBER(WalletDBInit);
// Hash node database holds nodes indexed by hash
const char *HashNodeDBInit[] = {
"BEGIN TRANSACTION;",
"CREATE TABLE CommittedObjects ( \
Hash CHARACTER(64) PRIMARY KEY, \
ObjType CHAR(1) NOT NULL, \
LedgerIndex BIGINT UNSIGNED, \
Object BLOB \
);",
"CREATE INDEX ObjectLocate ON \
CommittedObjects(LedgerIndex, ObjType);",
"END TRANSACTION;"
};
int HashNodeDBCount = NUMBER(HashNodeDBInit);
// Net node database holds nodes seen on the network
// XXX Not really used needs replacement.
const char *NetNodeDBInit[] = {
"CREATE TABLE KnownNodes ( \
Hanko CHARACTER(35) PRIMARY KEY, \
LastSeen TEXT, \
HaveContactInfo CHARACTER(1), \
ContactObject BLOB \
);"
};
int NetNodeDBCount = NUMBER(NetNodeDBInit);
// vim:ts=4

View File

@@ -0,0 +1,340 @@
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <openssl/ecdsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
// #define EC_DEBUG
// Functions to add CKey support for deterministic EC keys
#include <boost/test/unit_test.hpp>
#include "Serializer.h"
#include "Log.h"
// <-- seed
uint128 CKey::PassPhraseToKey(const std::string& passPhrase)
{
Serializer s;
s.addRaw(passPhrase);
uint256 hash256 = s.getSHA512Half();
uint128 ret(hash256);
s.secureErase();
return ret;
}
// --> seed
// <-- private root generator + public root generator
EC_KEY* CKey::GenerateRootDeterministicKey(const uint128& seed)
{
BN_CTX* ctx=BN_CTX_new();
if(!ctx) return NULL;
EC_KEY* pkey=EC_KEY_new_by_curve_name(NID_secp256k1);
if(!pkey)
{
BN_CTX_free(ctx);
return NULL;
}
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
BIGNUM* order=BN_new();
if(!order)
{
BN_CTX_free(ctx);
EC_KEY_free(pkey);
return NULL;
}
if(!EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
{
assert(false);
BN_free(order);
EC_KEY_free(pkey);
BN_CTX_free(ctx);
return NULL;
}
BIGNUM *privKey=NULL;
int seq=0;
do
{ // private key must be non-zero and less than the curve's order
Serializer s((128+32)/8);
s.add128(seed);
s.add32(seq++);
uint256 root=s.getSHA512Half();
s.secureErase();
privKey=BN_bin2bn((const unsigned char *) &root, sizeof(root), privKey);
if(privKey==NULL)
{
EC_KEY_free(pkey);
BN_free(order);
BN_CTX_free(ctx);
}
root.zero();
} while(BN_is_zero(privKey) || (BN_cmp(privKey, order)>=0));
BN_free(order);
if(!EC_KEY_set_private_key(pkey, privKey))
{ // set the random point as the private key
assert(false);
EC_KEY_free(pkey);
BN_clear_free(privKey);
BN_CTX_free(ctx);
return NULL;
}
EC_POINT *pubKey=EC_POINT_new(EC_KEY_get0_group(pkey));
if(!EC_POINT_mul(EC_KEY_get0_group(pkey), pubKey, privKey, NULL, NULL, ctx))
{ // compute the corresponding public key point
assert(false);
BN_clear_free(privKey);
EC_POINT_free(pubKey);
EC_KEY_free(pkey);
BN_CTX_free(ctx);
return NULL;
}
BN_clear_free(privKey);
if(!EC_KEY_set_public_key(pkey, pubKey))
{
assert(false);
EC_POINT_free(pubKey);
EC_KEY_free(pkey);
BN_CTX_free(ctx);
return NULL;
}
EC_POINT_free(pubKey);
BN_CTX_free(ctx);
#ifdef EC_DEBUG
assert(EC_KEY_check_key(pkey)==1); // CAUTION: This check is *very* expensive
#endif
return pkey;
}
// Take ripple address.
// --> root public generator (consumes)
// <-- root public generator in EC format
EC_KEY* CKey::GenerateRootPubKey(BIGNUM* pubGenerator)
{
if (pubGenerator == NULL)
{
assert(false);
return NULL;
}
EC_KEY* pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
if (!pkey)
{
BN_free(pubGenerator);
return NULL;
}
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
EC_POINT* pubPoint = EC_POINT_bn2point(EC_KEY_get0_group(pkey), pubGenerator, NULL, NULL);
BN_free(pubGenerator);
if(!pubPoint)
{
assert(false);
EC_KEY_free(pkey);
return NULL;
}
if(!EC_KEY_set_public_key(pkey, pubPoint))
{
assert(false);
EC_POINT_free(pubPoint);
EC_KEY_free(pkey);
return NULL;
}
return pkey;
}
// --> public generator
static BIGNUM* makeHash(const RippleAddress& pubGen, int seq, BIGNUM* order)
{
int subSeq=0;
BIGNUM* ret=NULL;
do
{
Serializer s((33*8+32+32)/8);
s.addRaw(pubGen.getGenerator());
s.add32(seq);
s.add32(subSeq++);
uint256 root=s.getSHA512Half();
s.secureErase();
ret=BN_bin2bn((const unsigned char *) &root, sizeof(root), ret);
if(!ret) return NULL;
} while (BN_is_zero(ret) || (BN_cmp(ret, order)>=0));
return ret;
}
// --> public generator
EC_KEY* CKey::GeneratePublicDeterministicKey(const RippleAddress& pubGen, int seq)
{ // publicKey(n) = rootPublicKey EC_POINT_+ Hash(pubHash|seq)*point
EC_KEY* rootKey = CKey::GenerateRootPubKey(pubGen.getGeneratorBN());
const EC_POINT* rootPubKey = EC_KEY_get0_public_key(rootKey);
BN_CTX* ctx = BN_CTX_new();
EC_KEY* pkey = EC_KEY_new_by_curve_name(NID_secp256k1);
EC_POINT* newPoint = 0;
BIGNUM* order = 0;
BIGNUM* hash = 0;
bool success = true;
if (!ctx || !pkey) success = false;
if (success)
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
if (success) {
newPoint = EC_POINT_new(EC_KEY_get0_group(pkey));
if(!newPoint) success = false;
}
if (success) {
order = BN_new();
if(!order || !EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
success = false;
}
// Calculate the private additional key.
if (success) {
hash = makeHash(pubGen, seq, order);
if(!hash) success = false;
}
if (success) {
// Calculate the corresponding public key.
EC_POINT_mul(EC_KEY_get0_group(pkey), newPoint, hash, NULL, NULL, ctx);
// Add the master public key and set.
EC_POINT_add(EC_KEY_get0_group(pkey), newPoint, newPoint, rootPubKey, ctx);
EC_KEY_set_public_key(pkey, newPoint);
}
if (order) BN_free(order);
if (hash) BN_free(hash);
if (newPoint) EC_POINT_free(newPoint);
if (ctx) BN_CTX_free(ctx);
if (rootKey) EC_KEY_free(rootKey);
if (pkey && !success) EC_KEY_free(pkey);
return success ? pkey : NULL;
}
EC_KEY* CKey::GeneratePrivateDeterministicKey(const RippleAddress& pubGen, const uint256& u, int seq)
{
CBigNum bn(u);
return GeneratePrivateDeterministicKey(pubGen, static_cast<BIGNUM*>(&bn), seq);
}
// --> root private key
EC_KEY* CKey::GeneratePrivateDeterministicKey(const RippleAddress& pubGen, const BIGNUM* rootPrivKey, int seq)
{ // privateKey(n) = (rootPrivateKey + Hash(pubHash|seq)) % order
BN_CTX* ctx=BN_CTX_new();
if(ctx==NULL) return NULL;
EC_KEY* pkey=EC_KEY_new_by_curve_name(NID_secp256k1);
if(pkey==NULL)
{
BN_CTX_free(ctx);
return NULL;
}
EC_KEY_set_conv_form(pkey, POINT_CONVERSION_COMPRESSED);
BIGNUM* order=BN_new();
if(order==NULL)
{
BN_CTX_free(ctx);
EC_KEY_free(pkey);
return NULL;
}
if(!EC_GROUP_get_order(EC_KEY_get0_group(pkey), order, ctx))
{
BN_free(order);
BN_CTX_free(ctx);
EC_KEY_free(pkey);
return NULL;
}
// calculate the private additional key
BIGNUM* privKey=makeHash(pubGen, seq, order);
if(privKey==NULL)
{
BN_free(order);
BN_CTX_free(ctx);
EC_KEY_free(pkey);
return NULL;
}
// calculate the final private key
BN_mod_add(privKey, privKey, rootPrivKey, order, ctx);
BN_free(order);
EC_KEY_set_private_key(pkey, privKey);
// compute the corresponding public key
EC_POINT* pubKey=EC_POINT_new(EC_KEY_get0_group(pkey));
if(!pubKey)
{
BN_clear_free(privKey);
BN_CTX_free(ctx);
EC_KEY_free(pkey);
return NULL;
}
if(EC_POINT_mul(EC_KEY_get0_group(pkey), pubKey, privKey, NULL, NULL, ctx)==0)
{
BN_clear_free(privKey);
BN_CTX_free(ctx);
EC_KEY_free(pkey);
return NULL;
}
BN_clear_free(privKey);
EC_KEY_set_public_key(pkey, pubKey);
EC_POINT_free(pubKey);
BN_CTX_free(ctx);
return pkey;
}
BOOST_AUTO_TEST_SUITE(DeterministicKeys_test)
BOOST_AUTO_TEST_CASE(DeterminsticKeys_test1)
{
Log(lsDEBUG) << "Beginning deterministic key test";
uint128 seed1, seed2;
seed1.SetHex("71ED064155FFADFA38782C5E0158CB26");
seed2.SetHex("CF0C3BE4485961858C4198515AE5B965");
CKey root1(seed1), root2(seed2);
uint256 priv1, priv2;
root1.GetPrivateKeyU(priv1);
root2.GetPrivateKeyU(priv2);
if (priv1.GetHex() != "7CFBA64F771E93E817E15039215430B53F7401C34931D111EAB3510B22DBB0D8")
BOOST_FAIL("Incorrect private key for generator");
if (priv2.GetHex() != "98BC2EACB26EB021D1A6293C044D88BA2F0B6729A2772DEEBF2E21A263C1740B")
BOOST_FAIL("Incorrect private key for generator");
RippleAddress nSeed;
nSeed.setSeed(seed1);
if (nSeed.humanSeed() != "shHM53KPZ87Gwdqarm1bAmPeXg8Tn")
BOOST_FAIL("Incorrect human seed");
if (nSeed.humanSeed1751() != "MAD BODY ACE MINT OKAY HUB WHAT DATA SACK FLAT DANA MATH")
BOOST_FAIL("Incorrect 1751 seed");
}
BOOST_AUTO_TEST_SUITE_END();
// vim:ts=4

303
src/cpp/ripple/ECIES.cpp Normal file
View File

@@ -0,0 +1,303 @@
#include <openssl/ec.h>
#include <openssl/bn.h>
#include <openssl/ecdsa.h>
#include <openssl/pem.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
#include <vector>
#include <cassert>
#include "key.h"
// ECIES uses elliptic curve keys to send an encrypted message.
// A shared secret is generated from one public key and one private key.
// The same key results regardless of which key is public and which private.
// Anonymous messages can be sent by generating an ephemeral public/private
// key pair, using that private key with the recipient's public key to
// encrypt and publishing the ephemeral public key. Non-anonymous messages
// can be sent by using your own private key with the recipient's public key.
// A random IV is used to encrypt the message and an HMAC is used to ensure
// message integrity. If you need timestamps or need to tell the recipient
// which key to use (his, yours, or ephemeral) you must add that data.
// (Obviously, key information can't go in the encrypted portion anyway.)
// Our ciphertext is all encrypted except the IV. The encrypted data decodes as follows:
// 1) IV (unencrypted)
// 2) Encrypted: HMAC of original plaintext
// 3) Encrypted: Original plaintext
// 4) Encrypted: Rest of block/padding
// ECIES operations throw on any error such as a corrupt message or incorrect
// key. They *must* be called in try/catch blocks.
// Algorithmic choices:
#define ECIES_KEY_HASH SHA512 // Hash used to expand shared secret
#define ECIES_KEY_LENGTH (512/8) // Size of expanded shared secret
#define ECIES_MIN_SEC (128/8) // The minimum equivalent security
#define ECIES_ENC_ALGO EVP_aes_256_cbc() // Encryption algorithm
#define ECIES_ENC_KEY_TYPE uint256 // Type used to hold shared secret
#define ECIES_ENC_KEY_SIZE (256/8) // Encryption key size
#define ECIES_ENC_BLK_SIZE (128/8) // Encryption block size
#define ECIES_ENC_IV_TYPE uint128 // Type used to hold IV
#define ECIES_HMAC_ALGO EVP_sha256() // HMAC algorithm
#define ECIES_HMAC_KEY_TYPE uint256 // Type used to hold HMAC key
#define ECIES_HMAC_KEY_SIZE (256/8) // Size of HMAC key
#define ECIES_HMAC_TYPE uint256 // Type used to hold HMAC value
#define ECIES_HMAC_SIZE (256/8) // Size of HMAC value
void CKey::getECIESSecret(CKey& otherKey, ECIES_ENC_KEY_TYPE& enc_key, ECIES_HMAC_KEY_TYPE& hmac_key)
{ // Retrieve a secret generated from an EC key pair. At least one private key must be known.
if (!pkey || !otherKey.pkey)
throw std::runtime_error("missing key");
EC_KEY *pubkey, *privkey;
if (EC_KEY_get0_private_key(pkey))
{
privkey = pkey;
pubkey = otherKey.pkey;
}
else if (EC_KEY_get0_private_key(otherKey.pkey))
{
privkey = otherKey.pkey;
pubkey = pkey;
}
else throw std::runtime_error("no private key");
unsigned char rawbuf[512];
int buflen = ECDH_compute_key(rawbuf, 512, EC_KEY_get0_public_key(pubkey), privkey, NULL);
if (buflen < ECIES_MIN_SEC)
throw std::runtime_error("ecdh key failed");
unsigned char hbuf[ECIES_KEY_LENGTH];
ECIES_KEY_HASH(rawbuf, buflen, hbuf);
memset(rawbuf, 0, ECIES_HMAC_KEY_SIZE);
assert((ECIES_ENC_KEY_SIZE + ECIES_HMAC_KEY_SIZE) >= ECIES_KEY_LENGTH);
memcpy(enc_key.begin(), hbuf, ECIES_ENC_KEY_SIZE);
memcpy(hmac_key.begin(), hbuf + ECIES_ENC_KEY_SIZE, ECIES_HMAC_KEY_SIZE);
memset(hbuf, 0, ECIES_KEY_LENGTH);
}
static ECIES_HMAC_TYPE makeHMAC(const ECIES_HMAC_KEY_TYPE& secret, const std::vector<unsigned char>& data)
{
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
if (HMAC_Init_ex(&ctx, secret.begin(), ECIES_HMAC_KEY_SIZE, ECIES_HMAC_ALGO, NULL) != 1)
{
HMAC_CTX_cleanup(&ctx);
throw std::runtime_error("init hmac");
}
if (HMAC_Update(&ctx, &(data.front()), data.size()) != 1)
{
HMAC_CTX_cleanup(&ctx);
throw std::runtime_error("update hmac");
}
ECIES_HMAC_TYPE ret;
unsigned int ml = ECIES_HMAC_SIZE;
if (HMAC_Final(&ctx, ret.begin(), &ml) != 1)
{
HMAC_CTX_cleanup(&ctx);
throw std::runtime_error("finalize hmac");
}
assert(ml == ECIES_HMAC_SIZE);
HMAC_CTX_cleanup(&ctx);
return ret;
}
std::vector<unsigned char> CKey::encryptECIES(CKey& otherKey, const std::vector<unsigned char>& plaintext)
{
ECIES_ENC_IV_TYPE iv;
if (RAND_bytes(static_cast<unsigned char *>(iv.begin()), ECIES_ENC_BLK_SIZE) != 1)
throw std::runtime_error("insufficient entropy");
ECIES_ENC_KEY_TYPE secret;
ECIES_HMAC_KEY_TYPE hmacKey;
getECIESSecret(otherKey, secret, hmacKey);
ECIES_HMAC_TYPE hmac = makeHMAC(hmacKey, plaintext);
hmacKey.zero();
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
if (EVP_EncryptInit_ex(&ctx, ECIES_ENC_ALGO, NULL, secret.begin(), iv.begin()) != 1)
{
EVP_CIPHER_CTX_cleanup(&ctx);
secret.zero();
throw std::runtime_error("init cipher ctx");
}
secret.zero();
std::vector<unsigned char> out(plaintext.size() + ECIES_HMAC_SIZE + ECIES_ENC_KEY_SIZE + ECIES_ENC_BLK_SIZE, 0);
int len = 0, bytesWritten;
// output IV
memcpy(&(out.front()), iv.begin(), ECIES_ENC_BLK_SIZE);
len = ECIES_ENC_BLK_SIZE;
// Encrypt/output HMAC
bytesWritten = out.capacity() - len;
assert(bytesWritten>0);
if (EVP_EncryptUpdate(&ctx, &(out.front()) + len, &bytesWritten, hmac.begin(), ECIES_HMAC_SIZE) < 0)
{
EVP_CIPHER_CTX_cleanup(&ctx);
throw std::runtime_error("");
}
len += bytesWritten;
// encrypt/output plaintext
bytesWritten = out.capacity() - len;
assert(bytesWritten>0);
if (EVP_EncryptUpdate(&ctx, &(out.front()) + len, &bytesWritten, &(plaintext.front()), plaintext.size()) < 0)
{
EVP_CIPHER_CTX_cleanup(&ctx);
throw std::runtime_error("");
}
len += bytesWritten;
// finalize
bytesWritten = out.capacity() - len;
if (EVP_EncryptFinal_ex(&ctx, &(out.front()) + len, &bytesWritten) < 0)
{
EVP_CIPHER_CTX_cleanup(&ctx);
throw std::runtime_error("encryption error");
}
len += bytesWritten;
// Output contains: IV, encrypted HMAC, encrypted data, encrypted padding
assert(len <= (plaintext.size() + ECIES_HMAC_SIZE + (2 * ECIES_ENC_BLK_SIZE)));
assert(len >= (plaintext.size() + ECIES_HMAC_SIZE + ECIES_ENC_BLK_SIZE)); // IV, HMAC, data
out.resize(len);
EVP_CIPHER_CTX_cleanup(&ctx);
return out;
}
std::vector<unsigned char> CKey::decryptECIES(CKey& otherKey, const std::vector<unsigned char>& ciphertext)
{
// minimum ciphertext = IV + HMAC + 1 block
if (ciphertext.size() < ((2 * ECIES_ENC_BLK_SIZE) + ECIES_HMAC_SIZE) )
throw std::runtime_error("ciphertext too short");
// extract IV
ECIES_ENC_IV_TYPE iv;
memcpy(iv.begin(), &(ciphertext.front()), ECIES_ENC_BLK_SIZE);
// begin decrypting
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
ECIES_ENC_KEY_TYPE secret;
ECIES_HMAC_KEY_TYPE hmacKey;
getECIESSecret(otherKey, secret, hmacKey);
if (EVP_DecryptInit_ex(&ctx, ECIES_ENC_ALGO, NULL, secret.begin(), iv.begin()) != 1)
{
secret.zero();
hmacKey.zero();
EVP_CIPHER_CTX_cleanup(&ctx);
throw std::runtime_error("unable to init cipher");
}
// decrypt mac
ECIES_HMAC_TYPE hmac;
int outlen = ECIES_HMAC_SIZE;
if ( (EVP_DecryptUpdate(&ctx, hmac.begin(), &outlen,
&(ciphertext.front()) + ECIES_ENC_BLK_SIZE, ECIES_HMAC_SIZE + 1) != 1) || (outlen != ECIES_HMAC_SIZE) )
{
secret.zero();
hmacKey.zero();
EVP_CIPHER_CTX_cleanup(&ctx);
throw std::runtime_error("unable to extract hmac");
}
// decrypt plaintext (after IV and encrypted mac)
std::vector<unsigned char> plaintext(ciphertext.size() - ECIES_HMAC_SIZE - ECIES_ENC_BLK_SIZE);
outlen = plaintext.size();
if (EVP_DecryptUpdate(&ctx, &(plaintext.front()), &outlen,
&(ciphertext.front()) + ECIES_ENC_BLK_SIZE + ECIES_HMAC_SIZE + 1,
ciphertext.size() - ECIES_ENC_BLK_SIZE - ECIES_HMAC_SIZE - 1) != 1)
{
secret.zero();
hmacKey.zero();
EVP_CIPHER_CTX_cleanup(&ctx);
throw std::runtime_error("unable to extract plaintext");
}
// decrypt padding
int flen = 0;
if (EVP_DecryptFinal(&ctx, &(plaintext.front()) + outlen, &flen) != 1)
{
secret.zero();
hmacKey.zero();
EVP_CIPHER_CTX_cleanup(&ctx);
throw std::runtime_error("plaintext had bad padding");
}
plaintext.resize(flen + outlen);
// verify integrity
if (hmac != makeHMAC(hmacKey, plaintext))
{
secret.zero();
hmacKey.zero();
EVP_CIPHER_CTX_cleanup(&ctx);
throw std::runtime_error("plaintext had bad hmac");
}
secret.zero();
hmacKey.zero();
EVP_CIPHER_CTX_cleanup(&ctx);
return plaintext;
}
bool checkECIES(void)
{
CKey senderPriv, recipientPriv, senderPub, recipientPub;
for (int i = 0; i < 30000; ++i)
{
if ((i % 100) == 0)
{ // generate new keys every 100 times
// std::cerr << "new keys" << std::endl;
senderPriv.MakeNewKey();
recipientPriv.MakeNewKey();
if (!senderPub.SetPubKey(senderPriv.GetPubKey()))
throw std::runtime_error("key error");
if (!recipientPub.SetPubKey(recipientPriv.GetPubKey()))
throw std::runtime_error("key error");
}
// generate message
std::vector<unsigned char> message(4096);
int msglen = i%3000;
if (RAND_bytes(static_cast<unsigned char *>(&message.front()), msglen) != 1)
throw std::runtime_error("insufficient entropy");
message.resize(msglen);
// encrypt message with sender's private key and recipient's public key
std::vector<unsigned char> ciphertext = senderPriv.encryptECIES(recipientPub, message);
// decrypt message with recipient's private key and sender's public key
std::vector<unsigned char> decrypt = recipientPriv.decryptECIES(senderPub, ciphertext);
if (decrypt != message)
{
assert(false);
return false;
}
// std::cerr << "Msg(" << msglen << ") ok " << ciphertext.size() << std::endl;
}
return true;
}
// vim:ts=4

View File

@@ -0,0 +1,143 @@
#include "FieldNames.h"
#include <map>
#include <boost/thread/mutex.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
#include "utils.h"
// These must stay at the top of this file
std::map<int, SField::ptr> SField::codeToField;
boost::mutex SField::mapMutex;
SField sfInvalid(-1), sfGeneric(0);
SField sfLedgerEntry(STI_LEDGERENTRY, 1, "LedgerEntry");
SField sfTransaction(STI_TRANSACTION, 1, "Transaction");
SField sfValidation(STI_VALIDATION, 1, "Validation");
SField sfHash(STI_HASH256, 257, "hash");
SField sfIndex(STI_HASH256, 258, "index");
#define FIELD(name, type, index) SField sf##name(FIELD_CODE(STI_##type, index), STI_##type, index, #name);
#define TYPE(name, type, index)
#include "SerializeProto.h"
#undef FIELD
#undef TYPE
static int initFields()
{
sfTxnSignature.notSigningField(); sfTxnSignatures.notSigningField();
sfSignature.notSigningField();
sfHighQualityIn.setMeta(SFM_CHANGE); sfHighQualityOut.setMeta(SFM_CHANGE);
sfLowQualityIn.setMeta(SFM_CHANGE); sfLowQualityOut.setMeta(SFM_CHANGE);
sfLowLimit.setMeta(SFM_ALWAYS); sfHighLimit.setMeta(SFM_ALWAYS);
sfTakerPays.setMeta(SFM_ALWAYS); sfTakerGets.setMeta(SFM_ALWAYS);
sfQualityIn.setMeta(SFM_ALWAYS); sfQualityOut.setMeta(SFM_ALWAYS);
sfBalance.setMeta(SFM_ALWAYS);
sfPublicKey.setMeta(SFM_CHANGE); sfMessageKey.setMeta(SFM_CHANGE);
sfSigningPubKey.setMeta(SFM_CHANGE); sfAuthorizedKey.setMeta(SFM_CHANGE);
sfSigningAccounts.setMeta(SFM_CHANGE);
sfWalletLocator.setMeta(SFM_ALWAYS);
sfWalletSize.setMeta(SFM_ALWAYS);
sfNickname.setMeta(SFM_CHANGE);
sfAmount.setMeta(SFM_ALWAYS);
sfDomain.setMeta(SFM_CHANGE);
sfOwner.setMeta(SFM_ALWAYS);
return 0;
}
static const int f = initFields();
SField::SField(SerializedTypeID tid, int fv) : fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv)
{ // call with the map mutex
fieldName = lexical_cast_i(tid) + "/" + lexical_cast_i(fv);
codeToField[fieldCode] = this;
}
SField::ref SField::getField(int code)
{
int type = code >> 16;
int field = code % 0xffff;
if ((type <= 0) || (field <= 0))
return sfInvalid;
boost::mutex::scoped_lock sl(mapMutex);
std::map<int, SField::ptr>::iterator it = codeToField.find(code);
if (it != codeToField.end())
return *(it->second);
if (field > 255) // don't dynamically extend types that have no binary encoding
return sfInvalid;
switch (type)
{ // types we are willing to dynamically extend
#define FIELD(name, type, index)
#define TYPE(name, type, index) case STI_##type:
#include "SerializeProto.h"
#undef FIELD
#undef TYPE
break;
default:
return sfInvalid;
}
return *(new SField(static_cast<SerializedTypeID>(type), field));
}
int SField::compare(SField::ref f1, SField::ref f2)
{ // -1 = f1 comes before f2, 0 = illegal combination, 1 = f1 comes after f2
if ((f1.fieldCode <= 0) || (f2.fieldCode <= 0))
return 0;
if (f1.fieldCode < f2.fieldCode)
return -1;
if (f2.fieldCode < f1.fieldCode)
return 1;
return 0;
}
std::string SField::getName() const
{
if (!fieldName.empty())
return fieldName;
if (fieldValue == 0)
return "";
return boost::lexical_cast<std::string>(static_cast<int>(fieldType)) + "/" +
boost::lexical_cast<std::string>(fieldValue);
}
SField::ref SField::getField(const std::string& fieldName)
{ // OPTIMIZEME me with a map. CHECKME this is case sensitive
boost::mutex::scoped_lock sl(mapMutex);
typedef std::pair<const int, SField::ptr> int_sfref_pair;
BOOST_FOREACH(const int_sfref_pair& fieldPair, codeToField)
{
if (fieldPair.second->fieldName == fieldName)
return *(fieldPair.second);
}
return sfInvalid;
}
SField::~SField()
{
boost::mutex::scoped_lock sl(mapMutex);
std::map<int, ptr>::iterator it = codeToField.find(fieldCode);
if ((it != codeToField.end()) && (it->second == this))
codeToField.erase(it);
}
// vim:ts=4

122
src/cpp/ripple/FieldNames.h Normal file
View File

@@ -0,0 +1,122 @@
#ifndef __FIELDNAMES__
#define __FIELDNAMES__
#include <string>
#include <boost/thread/mutex.hpp>
#define FIELD_CODE(type, index) ((static_cast<int>(type) << 16) | index)
enum SerializedTypeID
{
// special types
STI_UNKNOWN = -2,
STI_DONE = -1,
STI_NOTPRESENT = 0,
#define TYPE(name, field, value) STI_##field = value,
#define FIELD(name, field, value)
#include "SerializeProto.h"
#undef TYPE
#undef FIELD
// high level types
STI_TRANSACTION = 10001,
STI_LEDGERENTRY = 10002,
STI_VALIDATION = 10003,
};
enum SOE_Flags
{
SOE_INVALID = -1,
SOE_REQUIRED = 0, // required
SOE_OPTIONAL = 1, // optional
};
enum SF_Meta
{
SFM_NEVER = 0,
SFM_CHANGE = 1,
SFM_DELETE = 2,
SFM_ALWAYS = 3
};
class SField
{
public:
typedef const SField& ref;
typedef SField const * ptr;
protected:
static std::map<int, ptr> codeToField;
static boost::mutex mapMutex;
SField(SerializedTypeID id, int val);
public:
const int fieldCode; // (type<<16)|index
const SerializedTypeID fieldType; // STI_*
const int fieldValue; // Code number for protocol
std::string fieldName;
SF_Meta fieldMeta;
bool signingField;
SField(int fc, SerializedTypeID tid, int fv, const char* fn) :
fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER), signingField(true)
{
boost::mutex::scoped_lock sl(mapMutex);
codeToField[fieldCode] = this;
}
SField(SerializedTypeID tid, int fv, const char *fn) :
fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv), fieldName(fn),
fieldMeta(SFM_NEVER), signingField(true)
{
boost::mutex::scoped_lock sl(mapMutex);
codeToField[fieldCode] = this;
}
SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0) { ; }
~SField();
static SField::ref getField(int fieldCode);
static SField::ref getField(const std::string& fieldName);
static SField::ref getField(int type, int value) { return getField(FIELD_CODE(type, value)); }
static SField::ref getField(SerializedTypeID type, int value) { return getField(FIELD_CODE(type, value)); }
std::string getName() const;
bool hasName() const { return !fieldName.empty(); }
bool isGeneric() const { return fieldCode == 0; }
bool isInvalid() const { return fieldCode == -1; }
bool isKnown() const { return fieldType != STI_UNKNOWN; }
bool isBinary() const { return fieldValue < 256; }
bool isDiscardable() const { return fieldValue > 256; }
SF_Meta getMeta() const { return fieldMeta; }
bool shouldMetaDel() const { return (fieldMeta == SFM_DELETE) || (fieldMeta == SFM_ALWAYS); }
bool shouldMetaMod() const { return (fieldMeta == SFM_CHANGE) || (fieldMeta == SFM_ALWAYS); }
void setMeta(SF_Meta m) { fieldMeta = m; }
bool isSigningField() const { return signingField; }
void notSigningField() { signingField = false; }
bool shouldInclude(bool withSigningField) const
{ return (fieldValue < 256) && (withSigningField || signingField); }
bool operator==(const SField& f) const { return fieldCode == f.fieldCode; }
bool operator!=(const SField& f) const { return fieldCode != f.fieldCode; }
static int compare(SField::ref f1, SField::ref f2);
};
extern SField sfInvalid, sfGeneric, sfLedgerEntry, sfTransaction, sfValidation;
#define FIELD(name, type, index) extern SField sf##name;
#define TYPE(name, type, index)
#include "SerializeProto.h"
#undef FIELD
#undef TYPE
#endif

View File

@@ -0,0 +1,102 @@
#include "HTTPRequest.h"
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "Log.h"
SETUP_LOG();
void HTTPRequest::reset()
{
vHeaders.clear();
sRequestBody.clear();
sAuthorization.clear();
iDataSize = 0;
bShouldClose = true;
eState = await_request;
}
HTTPRequestAction HTTPRequest::requestDone(bool forceClose)
{
if (forceClose || bShouldClose)
return haCLOSE_CONN;
reset();
return haREAD_LINE;
}
std::string HTTPRequest::getReplyHeaders(bool forceClose)
{
if (forceClose || bShouldClose)
return "Connection: close\r\n";
else
return "Connection: Keep-Alive\r\n";
}
HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf)
{
std::string line;
std::istream is(&buf);
std::getline(is, line);
boost::trim(line);
// cLog(lsTRACE) << "HTTPRequest line: " << line;
if (eState == await_request)
{ // VERB URL PROTO
if (line.empty())
return haREAD_LINE;
sRequest = line;
bShouldClose = sRequest.find("HTTP/1.1") == std::string::npos;
eState = await_header;
return haREAD_LINE;
}
if (eState == await_header)
{ // HEADER_NAME: HEADER_BODY
if (line.empty()) // empty line or bare \r
{
if (iDataSize == 0)
{ // no body
eState = do_request;
return haDO_REQUEST;
}
eState = getting_body;
return haREAD_RAW;
}
vHeaders.push_back(line);
size_t colon = line.find(':');
if (colon != std::string::npos)
{
std::string headerName = line.substr(0, colon);
boost::trim(headerName);
boost::to_lower(headerName);
std::string headerValue = line.substr(colon+1);
boost::trim(headerValue);
if (headerName == "connection")
{
boost::to_lower(headerValue);
if ((headerValue == "keep-alive") || (headerValue == "keepalive"))
bShouldClose = false;
if (headerValue == "close")
bShouldClose = true;
}
if (headerName == "content-length")
iDataSize = boost::lexical_cast<int>(headerValue);
if (headerName == "authorization")
sAuthorization = headerValue;
}
return haREAD_LINE;
}
assert(false);
return haERROR;
}

View File

@@ -0,0 +1,61 @@
#ifndef HTTPREQUEST__HPP
#define HTTPREQUEST__HPP
#include <string>
#include <vector>
#include <boost/asio/streambuf.hpp>
enum HTTPRequestAction
{ // What the application code needs to do
haERROR = 0,
haREAD_LINE = 1,
haREAD_RAW = 2,
haDO_REQUEST = 3,
haCLOSE_CONN = 4
};
class HTTPRequest
{ // an HTTP request in progress
protected:
enum state
{
await_request, // We are waiting for the request line
await_header, // We are waiting for request headers
getting_body, // We are waiting for the body
do_request, // We are waiting for the request to complete
};
state eState;
std::string sRequest; // VERB URL PROTO
std::string sRequestBody;
std::string sAuthorization;
std::vector<std::string> vHeaders;
int iDataSize;
bool bShouldClose;
public:
HTTPRequest() : eState(await_request), iDataSize(0), bShouldClose(true) { ; }
void reset();
std::string& peekBody() { return sRequestBody; }
std::string getBody() { return sRequestBody; }
std::string& peekRequest() { return sRequest; }
std::string getRequest() { return sRequest; }
std::string& peekAuth() { return sAuthorization; }
std::string getAuth() { return sAuthorization; }
std::vector<std::string>& peekHeaders() { return vHeaders; }
std::string getReplyHeaders(bool forceClose);
HTTPRequestAction consume(boost::asio::streambuf&);
HTTPRequestAction requestDone(bool forceClose); // call after reply is sent
int getDataSize() { return iDataSize; }
};
#endif

View File

@@ -0,0 +1,32 @@
#ifndef __HASHPREFIXES__
#define __HASHPREFIXES__
#include "types.h"
// TXN - Hash of transaction plus signature to give transaction ID
const uint32 sHP_TransactionID = 0x54584E00;
// STX - Hash of inner transaction to sign
const uint32 sHP_TransactionSign = 0x53545800;
// TND - Hash of transaction plus metadata
const uint32 sHP_TransactionNode = 0x534E4400;
// MLN - Hash of account state
const uint32 sHP_LeafNode = 0x4D4C4E00;
// MIN - Hash of inner node in tree
const uint32 sHP_InnerNode = 0x4D494E00;
// LGR - Hash of ledger master data for signing
const uint32 sHP_Ledger = 0x4C575200;
// VAL - Hash of validation for signing
const uint32 sHP_Validation = 0x56414C00;
// PRP - Hash of proposal for signing
const uint32 sHP_Proposal = 0x50525000;
#endif
// vim:ts=4

View File

@@ -0,0 +1,175 @@
#include "HashedObject.h"
#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
#include "Serializer.h"
#include "Application.h"
#include "Log.h"
SETUP_LOG();
DECLARE_INSTANCE(HashedObject);
HashedObjectStore::HashedObjectStore(int cacheSize, int cacheAge) :
mCache("HashedObjectStore", cacheSize, cacheAge), mWritePending(false)
{
mWriteSet.reserve(128);
}
bool HashedObjectStore::store(HashedObjectType type, uint32 index,
const std::vector<unsigned char>& data, const uint256& hash)
{ // return: false = already in cache, true = added to cache
if (!theApp->getHashNodeDB())
{
cLog(lsTRACE) << "HOS: no db";
return true;
}
if (mCache.touch(hash))
{
cLog(lsTRACE) << "HOS: " << hash << " store: incache";
return false;
}
assert(hash == Serializer::getSHA512Half(data));
HashedObject::pointer object = boost::make_shared<HashedObject>(type, index, data, hash);
if (!mCache.canonicalize(hash, object))
{
// cLog(lsTRACE) << "Queuing write for " << hash;
boost::mutex::scoped_lock sl(mWriteMutex);
mWriteSet.push_back(object);
if (!mWritePending)
{
mWritePending = true;
boost::thread t(boost::bind(&HashedObjectStore::bulkWrite, this));
t.detach();
}
}
// else
// cLog(lsTRACE) << "HOS: already had " << hash;
return true;
}
void HashedObjectStore::waitWrite()
{
boost::unique_lock<boost::mutex> sl(mWriteMutex);
while (mWritePending)
mWriteCondition.wait(sl);
}
void HashedObjectStore::bulkWrite()
{
while (1)
{
std::vector< boost::shared_ptr<HashedObject> > set;
set.reserve(128);
{
boost::unique_lock<boost::mutex> sl(mWriteMutex);
mWriteSet.swap(set);
assert(mWriteSet.empty());
if (set.empty())
{
mWritePending = false;
mWriteCondition.notify_all();
return;
}
}
// cLog(lsTRACE) << "HOS: writing " << set.size();
static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';");
static boost::format
fAdd("INSERT INTO CommittedObjects (Hash,ObjType,LedgerIndex,Object) VALUES ('%s','%c','%u',%s);");
Database* db = theApp->getHashNodeDB()->getDB();
ScopedLock sl = theApp->getHashNodeDB()->getDBLock();
db->executeSQL("BEGIN TRANSACTION;");
BOOST_FOREACH(const boost::shared_ptr<HashedObject>& it, set)
{
if (!SQL_EXISTS(db, boost::str(fExists % it->getHash().GetHex())))
{
char type;
switch(it->getType())
{
case hotLEDGER: type = 'L'; break;
case hotTRANSACTION: type = 'T'; break;
case hotACCOUNT_NODE: type = 'A'; break;
case hotTRANSACTION_NODE: type = 'N'; break;
default: type = 'U';
}
std::string rawData;
db->escape(&(it->getData().front()), it->getData().size(), rawData);
db->executeSQL(boost::str(fAdd % it->getHash().GetHex() % type % it->getIndex() % rawData ));
}
}
db->executeSQL("END TRANSACTION;");
}
}
HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
{
HashedObject::pointer obj;
{
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
obj = mCache.fetch(hash);
if (obj)
{
cLog(lsTRACE) << "HOS: " << hash << " fetch: incache";
return obj;
}
}
if (!theApp || !theApp->getHashNodeDB())
return HashedObject::pointer();
std::string sql = "SELECT * FROM CommittedObjects WHERE Hash='";
sql.append(hash.GetHex());
sql.append("';");
std::vector<unsigned char> data;
{
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
Database* db = theApp->getHashNodeDB()->getDB();
if (!db->executeSQL(sql) || !db->startIterRows())
{
// cLog(lsTRACE) << "HOS: " << hash << " fetch: not in db";
return HashedObject::pointer();
}
std::string type;
db->getStr("ObjType", type);
if (type.size() == 0) return HashedObject::pointer();
uint32 index = db->getBigInt("LedgerIndex");
int size = db->getBinary("Object", NULL, 0);
data.resize(size);
db->getBinary("Object", &(data.front()), size);
db->endIterRows();
assert(Serializer::getSHA512Half(data) == hash);
HashedObjectType htype = hotUNKNOWN;
switch (type[0])
{
case 'L': htype = hotLEDGER; break;
case 'T': htype = hotTRANSACTION; break;
case 'A': htype = hotACCOUNT_NODE; break;
case 'N': htype = hotTRANSACTION_NODE; break;
default:
cLog(lsERROR) << "Invalid hashed object";
return HashedObject::pointer();
}
obj = boost::make_shared<HashedObject>(htype, index, data, hash);
mCache.canonicalize(hash, obj);
}
cLog(lsTRACE) << "HOS: " << hash << " fetch: in db";
return obj;
}
// vim:ts=4

View File

@@ -0,0 +1,70 @@
#ifndef __HASHEDOBJECT__
#define __HASHEDOBJECT__
#include <vector>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include "types.h"
#include "uint256.h"
#include "ScopedLock.h"
#include "TaggedCache.h"
#include "InstanceCounter.h"
DEFINE_INSTANCE(HashedObject);
enum HashedObjectType
{
hotUNKNOWN = 0,
hotLEDGER = 1,
hotTRANSACTION = 2,
hotACCOUNT_NODE = 3,
hotTRANSACTION_NODE = 4
};
class HashedObject : private IS_INSTANCE(HashedObject)
{
public:
typedef boost::shared_ptr<HashedObject> pointer;
HashedObjectType mType;
uint256 mHash;
uint32 mLedgerIndex;
std::vector<unsigned char> mData;
HashedObject(HashedObjectType type, uint32 index, const std::vector<unsigned char>& data, const uint256& hash) :
mType(type), mHash(hash), mLedgerIndex(index), mData(data) { ; }
const std::vector<unsigned char>& getData() const { return mData; }
const uint256& getHash() const { return mHash; }
HashedObjectType getType() const { return mType; }
uint32 getIndex() const { return mLedgerIndex; }
};
class HashedObjectStore
{
protected:
TaggedCache<uint256, HashedObject> mCache;
boost::mutex mWriteMutex;
boost::condition_variable mWriteCondition;
std::vector< boost::shared_ptr<HashedObject> > mWriteSet;
bool mWritePending;
public:
HashedObjectStore(int cacheSize, int cacheAge);
bool store(HashedObjectType type, uint32 index, const std::vector<unsigned char>& data,
const uint256& hash);
HashedObject::pointer retrieve(const uint256& hash);
void bulkWrite();
void waitWrite();
void sweep() { mCache.sweep(); }
};
#endif

View File

@@ -0,0 +1,383 @@
//
// Fetch a web page via https.
//
#include "HttpsClient.h"
#include "utils.h"
#include <iostream>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/regex.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/system/error_code.hpp>
using namespace boost::system;
using namespace boost::asio;
HttpsClient::HttpsClient(
boost::asio::io_service& io_service,
const unsigned short port,
const std::string& strPath,
std::size_t responseMax
) :
mCtx(boost::asio::ssl::context::sslv23),
mResolver(io_service),
mSocketSsl(io_service, mCtx),
mResponse(responseMax),
mStrPath(strPath),
mPort(port),
mDeadline(io_service)
{
}
void HttpsClient::httpsGet(
std::deque<std::string> deqSites,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
mDeqSites = deqSites;
mComplete = complete;
mTimeout = timeout;
httpsNext();
}
void HttpsClient::httpsNext()
{
// std::cerr << "Fetch: " << mDeqSites[0] << std::endl;
boost::shared_ptr<boost::asio::ip::tcp::resolver::query> query(new boost::asio::ip::tcp::resolver::query(mDeqSites[0], boost::lexical_cast<std::string>(mPort),
ip::resolver_query_base::numeric_service|ip::resolver_query_base::numeric_service));
mQuery = query;
mCtx.set_default_verify_paths(mShutdown);
if (mShutdown)
{
std::cerr << "set_default_verify_paths: " << mShutdown.message() << std::endl;
}
if (!mShutdown)
{
mDeadline.expires_from_now(mTimeout, mShutdown);
// std::cerr << "expires_from_now: " << mShutdown.message() << std::endl;
}
if (!mShutdown)
{
mDeadline.async_wait(
boost::bind(
&HttpsClient::ShandleDeadline,
shared_from_this(),
boost::asio::placeholders::error));
}
if (!mShutdown)
{
// std::cerr << "Resolving: " << mDeqSites[0] << std::endl;
mResolver.async_resolve(*mQuery,
boost::bind(
&HttpsClient::ShandleResolve,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::iterator));
}
if (mShutdown)
invokeComplete(mShutdown);
}
void HttpsClient::handleDeadline(const boost::system::error_code& ecResult)
{
if (ecResult == boost::asio::error::operation_aborted)
{
// Timer canceled because deadline no longer needed.
// std::cerr << "Deadline cancelled." << std::endl;
nothing(); // Aborter is done.
}
else if (ecResult)
{
std::cerr << "Deadline error: " << mDeqSites[0] << ": " << ecResult.message() << std::endl;
// Can't do anything sound.
abort();
}
else
{
boost::system::error_code ec_shutdown;
std::cerr << "Deadline arrived." << std::endl;
// Mark us as shutting down.
// XXX Use our own error code.
mShutdown = boost::system::error_code(errc::bad_address, system_category());
// Cancel any resolving.
mResolver.cancel();
// Stop the transaction.
mSocketSsl.shutdown(ec_shutdown);
if (ec_shutdown)
{
std::cerr << "Shutdown error: " << mDeqSites[0] << ": " << ec_shutdown.message() << std::endl;
}
}
}
void HttpsClient::handleResolve(
const boost::system::error_code& ecResult,
boost::asio::ip::tcp::resolver::iterator itrEndpoint
)
{
if (!mShutdown)
mShutdown = ecResult;
if (mShutdown)
{
// std::cerr << "Resolve error: " << mDeqSites[0] << ": " << mShutdown.message() << std::endl;
invokeComplete(mShutdown);
}
else
{
// std::cerr << "Resolve complete." << std::endl;
boost::asio::async_connect(
mSocketSsl.lowest_layer(),
itrEndpoint,
boost::bind(
&HttpsClient::ShandleConnect,
shared_from_this(),
boost::asio::placeholders::error));
}
}
void HttpsClient::handleConnect(const boost::system::error_code& ecResult)
{
if (!mShutdown)
mShutdown = ecResult;
if (mShutdown)
{
std::cerr << "Connect error: " << mShutdown.message() << std::endl;
}
if (!mShutdown)
{
// std::cerr << "Connected." << std::endl;
mSocketSsl.set_verify_mode(boost::asio::ssl::verify_peer);
// XXX Verify semantics of RFC 2818 are what we want.
mSocketSsl.set_verify_callback(boost::asio::ssl::rfc2818_verification(mDeqSites[0]), mShutdown);
if (mShutdown)
{
std::cerr << "set_verify_callback: " << mDeqSites[0] << ": " << mShutdown.message() << std::endl;
}
}
if (!mShutdown)
{
mSocketSsl.async_handshake(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>::client,
boost::bind(&HttpsClient::ShandleRequest,
shared_from_this(),
boost::asio::placeholders::error));
}
else
{
invokeComplete(mShutdown);
}
}
void HttpsClient::handleRequest(const boost::system::error_code& ecResult)
{
if (!mShutdown)
mShutdown = ecResult;
if (mShutdown)
{
std::cerr << "Handshake error:" << mShutdown.message() << std::endl;
invokeComplete(mShutdown);
}
else
{
// std::cerr << "SSL session started." << std::endl;
std::ostream osRequest(&mRequest);
osRequest <<
"GET " << mStrPath << " HTTP/1.0\r\n"
"Host: " << mDeqSites[0] << "\r\n"
"Accept: */*\r\n" // YYY Do we need this line?
"Connection: close\r\n\r\n";
boost::asio::async_write(
mSocketSsl,
mRequest,
boost::bind(&HttpsClient::ShandleWrite,
shared_from_this(),
boost::asio::placeholders::error));
}
}
void HttpsClient::handleWrite(const boost::system::error_code& ecResult)
{
if (!mShutdown)
mShutdown = ecResult;
if (mShutdown)
{
std::cerr << "Write error: " << mShutdown.message() << std::endl;
invokeComplete(mShutdown);
}
else
{
// std::cerr << "Wrote." << std::endl;
boost::asio::async_read(
mSocketSsl,
mResponse,
boost::asio::transfer_all(),
boost::bind(&HttpsClient::ShandleData,
shared_from_this(),
boost::asio::placeholders::error));
}
}
void HttpsClient::handleData(const boost::system::error_code& ecResult)
{
if (!mShutdown)
mShutdown = ecResult;
if (mShutdown && mShutdown != boost::asio::error::eof)
{
std::cerr << "Read error: " << mShutdown.message() << std::endl;
invokeComplete(mShutdown);
}
else
{
if (mShutdown)
{
// std::cerr << "Complete." << std::endl;
nothing();
}
else
{
// XXX According to boost example code, this is what we should expect for success.
std::cerr << "Complete, no eof." << std::endl;
}
parseData();
}
}
// Call cancel the deadline timer and invoke the completion routine.
void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, std::string strData)
{
boost::system::error_code ecCancel;
(void) mDeadline.cancel(ecCancel);
if (ecCancel)
{
std::cerr << "Deadline cancel error: " << ecCancel.message() << std::endl;
}
mDeqSites.pop_front();
if (mDeqSites.empty())
{
mComplete(ecResult ? ecResult : ecCancel, strData);
}
else
{
httpsNext();
}
}
void HttpsClient::parseData()
{
std::string strData((std::istreambuf_iterator<char>(&mResponse)), std::istreambuf_iterator<char>());
static boost::regex reStatus("\\`HTTP/1\\S+ (\\d{3}) .*\\'"); // HTTP/1.1 200 OK
static boost::regex reBody("\\`(?:.*?\\r\\n\\r\\n){1}(.*)\\'");
boost::smatch smMatch;
bool bMatch = boost::regex_match(strData, smMatch, reStatus) // Match status code.
&& !smMatch[1].compare("200")
&& boost::regex_match(strData, smMatch, reBody); // Match body.
// std::cerr << "Data:" << strData << std::endl;
// std::cerr << "Match: " << bMatch << std::endl;
// std::cerr << "Body:" << smMatch[1] << std::endl;
if (bMatch)
{
boost::system::error_code noErr;
invokeComplete(noErr, smMatch[1]);
}
else
{
// XXX Use our own error code.
invokeComplete(boost::system::error_code(errc::bad_address, system_category()));
}
}
void HttpsClient::httpsGet(
boost::asio::io_service& io_service,
std::deque<std::string> deqSites,
const unsigned short port,
const std::string& strPath,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, strPath, responseMax));
client->httpsGet(deqSites, timeout, complete);
}
void HttpsClient::httpsGet(
boost::asio::io_service& io_service,
std::string strSite,
const unsigned short port,
const std::string& strPath,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
std::deque<std::string> deqSites(1, strSite);
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, strPath, responseMax));
client->httpsGet(deqSites, timeout, complete);
}
bool HttpsClient::httpsParseUrl(const std::string& strUrl, std::string& strDomain, std::string& strPath)
{
static boost::regex reUrl("(?i)\\`\\s*https://([^/]+)(/.*)\\s*\\'"); // https://DOMAINPATH
boost::smatch smMatch;
bool bMatch = boost::regex_match(strUrl, smMatch, reUrl); // Match status code.
if (bMatch)
{
strDomain = smMatch[1];
strPath = smMatch[2];
}
// std::cerr << strUrl << " : " << bMatch << " : '" << strDomain << "' : '" << strPath << "'" << std::endl;
return bMatch;
}
// vim:ts=4

View File

@@ -0,0 +1,106 @@
#ifndef _HTTPS_CLIENT_
#define _HTTPS_CLIENT_
#include <deque>
#include <string>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
//
// Async https client.
//
class HttpsClient : public boost::enable_shared_from_this<HttpsClient>
{
private:
typedef boost::shared_ptr<HttpsClient> pointer;
boost::asio::ssl::context mCtx;
boost::asio::ip::tcp::resolver mResolver;
boost::shared_ptr<boost::asio::ip::tcp::resolver::query> mQuery;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> mSocketSsl;
boost::asio::streambuf mRequest;
boost::asio::streambuf mResponse;
const std::string mStrPath;
const unsigned short mPort;
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> mComplete;
boost::asio::deadline_timer mDeadline;
// If not success, we are shutting down.
boost::system::error_code mShutdown;
std::deque<std::string> mDeqSites;
boost::posix_time::time_duration mTimeout;
void handleDeadline(const boost::system::error_code& ecResult);
static void ShandleDeadline(pointer This, const boost::system::error_code& ecResult)
{ This->handleDeadline(ecResult); }
void handleResolve(const boost::system::error_code& ecResult, boost::asio::ip::tcp::resolver::iterator endpoint_iterator);
static void ShandleResolve(pointer This, const boost::system::error_code& ecResult, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{ This->handleResolve(ecResult, endpoint_iterator); }
void handleConnect(const boost::system::error_code& ecResult);
static void ShandleConnect(pointer This, const boost::system::error_code& ecResult)
{ This->handleConnect(ecResult); }
void handleRequest(const boost::system::error_code& ecResult);
static void ShandleRequest(pointer This, const boost::system::error_code& ecResult)
{ This->handleRequest(ecResult); }
void handleWrite(const boost::system::error_code& ecResult);
static void ShandleWrite(pointer This, const boost::system::error_code& ecResult)
{ This->handleWrite(ecResult); }
void handleData(const boost::system::error_code& ecResult);
static void ShandleData(pointer This, const boost::system::error_code& ecResult)
{ This->handleData(ecResult); }
void parseData();
void httpsNext();
void invokeComplete(const boost::system::error_code& ecResult, std::string strData = "");
public:
HttpsClient(
boost::asio::io_service& io_service,
const unsigned short port,
const std::string& strPath,
std::size_t responseMax
);
void httpsGet(
std::deque<std::string> deqSites,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
static void httpsGet(
boost::asio::io_service& io_service,
std::deque<std::string> deqSites,
const unsigned short port,
const std::string& strPath,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
static void httpsGet(
boost::asio::io_service& io_service,
std::string strSite,
const unsigned short port,
const std::string& strPath,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
static bool httpsParseUrl(const std::string& strUrl, std::string& strDomain, std::string& strPath);
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,15 @@
#include "InstanceCounter.h"
InstanceType* InstanceType::sHeadInstance = NULL;
std::vector<InstanceType::InstanceCount> InstanceType::getInstanceCounts(int min)
{
std::vector<InstanceCount> ret;
for (InstanceType* i = sHeadInstance; i != NULL; i = i->mNextInstance)
{
int c = i->getCount();
if (c >= min)
ret.push_back(InstanceCount(i->getName(), c));
}
return ret;
}

View File

@@ -0,0 +1,80 @@
#ifndef INSTANCE_COUNTER__H
#define INSTANCE_COUNTER__H
#include <string>
#include <vector>
#include <boost/thread/mutex.hpp>
#define DEFINE_INSTANCE(x) \
extern InstanceType IT_##x; \
class Instance_##x : private Instance \
{ \
protected: \
Instance_##x() : Instance(IT_##x) { ; } \
Instance_##x(const Instance_##x &) : \
Instance(IT_##x) { ; } \
Instance_##x& operator=(const Instance_##x&) \
{ return *this; } \
}
#define DECLARE_INSTANCE(x) \
InstanceType IT_##x(#x);
#define IS_INSTANCE(x) Instance_##x
class InstanceType
{
protected:
int mInstances;
std::string mName;
boost::mutex mLock;
InstanceType* mNextInstance;
static InstanceType* sHeadInstance;
public:
typedef std::pair<std::string, int> InstanceCount;
InstanceType(const char *n) : mInstances(0), mName(n)
{
mNextInstance = sHeadInstance;
sHeadInstance = this;
}
void addInstance()
{
mLock.lock();
++mInstances;
mLock.unlock();
}
void decInstance()
{
mLock.lock();
--mInstances;
mLock.unlock();
}
int getCount()
{
boost::mutex::scoped_lock(mLock);
return mInstances;
}
const std::string& getName()
{
return mName;
}
static std::vector<InstanceCount> getInstanceCounts(int min = 1);
};
class Instance
{
protected:
InstanceType& mType;
public:
Instance(InstanceType& t) : mType(t) { mType.addInstance(); }
~Instance() { mType.decInstance(); }
};
#endif

View File

@@ -0,0 +1,202 @@
#include "Interpreter.h"
#include "Operation.h"
#include "Config.h"
/*
We also need to charge for each op
*/
namespace Script {
Interpreter::Interpreter()
{
mContract=NULL;
mCode=NULL;
mInstructionPointer=0;
mTotalFee=0;
mInBlock=false;
mBlockSuccess=true;
mBlockJump=0;
mFunctionTable.resize(NUM_OF_OPS);
mFunctionTable[INT_OP]=new IntOp();
mFunctionTable[FLOAT_OP]=new FloatOp();
mFunctionTable[UINT160_OP]=new Uint160Op();
mFunctionTable[BOOL_OP]=new Uint160Op();
mFunctionTable[PATH_OP]=new Uint160Op();
mFunctionTable[ADD_OP]=new AddOp();
mFunctionTable[SUB_OP]=new SubOp();
mFunctionTable[MUL_OP]=new MulOp();
mFunctionTable[DIV_OP]=new DivOp();
mFunctionTable[MOD_OP]=new ModOp();
mFunctionTable[GTR_OP]=new GtrOp();
mFunctionTable[LESS_OP]=new LessOp();
mFunctionTable[EQUAL_OP]=new SubOp();
mFunctionTable[NOT_EQUAL_OP]=new SubOp();
mFunctionTable[AND_OP]=new SubOp();
mFunctionTable[OR_OP]=new SubOp();
mFunctionTable[NOT_OP]=new SubOp();
mFunctionTable[JUMP_OP]=new SubOp();
mFunctionTable[JUMPIF_OP]=new SubOp();
mFunctionTable[STOP_OP]=new SubOp();
mFunctionTable[CANCEL_OP]=new SubOp();
mFunctionTable[BLOCK_OP]=new SubOp();
mFunctionTable[BLOCK_END_OP]=new SubOp();
mFunctionTable[SEND_XRP_OP]=new SendXRPOp();
/*
mFunctionTable[SEND_OP]=new SendOp();
mFunctionTable[REMOVE_CONTRACT_OP]=new SubOp();
mFunctionTable[FEE_OP]=new SubOp();
mFunctionTable[CHANGE_CONTRACT_OWNER_OP]=new SubOp();
mFunctionTable[STOP_REMOVE_OP]=new SubOp();
mFunctionTable[SET_DATA_OP]=new SubOp();
mFunctionTable[GET_DATA_OP]=new SubOp();
mFunctionTable[GET_NUM_DATA_OP]=new SubOp();
mFunctionTable[SET_REGISTER_OP]=new SubOp();
mFunctionTable[GET_REGISTER_OP]=new SubOp();
mFunctionTable[GET_ISSUER_ID_OP]=new SubOp();
mFunctionTable[GET_OWNER_ID_OP]=new SubOp();
mFunctionTable[GET_LEDGER_TIME_OP]=new SubOp();
mFunctionTable[GET_LEDGER_NUM_OP]=new SubOp();
mFunctionTable[GET_RAND_FLOAT_OP]=new SubOp();
mFunctionTable[GET_XRP_ESCROWED_OP]=new SubOp();
mFunctionTable[GET_RIPPLE_ESCROWED_OP]=new SubOp();
mFunctionTable[GET_RIPPLE_ESCROWED_CURRENCY_OP]=new SubOp();
mFunctionTable[GET_RIPPLE_ESCROWED_ISSUER]=new GetRippleEscrowedIssuerOp();
mFunctionTable[GET_ACCEPT_DATA_OP]=new AcceptDataOp();
mFunctionTable[GET_ACCEPTOR_ID_OP]=new GetAcceptorIDOp();
mFunctionTable[GET_CONTRACT_ID_OP]=new GetContractIDOp();
*/
}
Data::pointer Interpreter::popStack()
{
if(mStack.size())
{
Data::pointer item=mStack[mStack.size()-1];
mStack.pop_back();
return(item);
}else
{
return(Data::pointer(new ErrorData()));
}
}
void Interpreter::pushStack(Data::pointer data)
{
mStack.push_back(data);
}
// offset is where to jump to if the block fails
bool Interpreter::startBlock(int offset)
{
if(mInBlock) return(false); // can't nest blocks
mBlockSuccess=true;
mInBlock=true;
mBlockJump=offset+mInstructionPointer;
return(true);
}
bool Interpreter::endBlock()
{
if(!mInBlock) return(false);
mInBlock=false;
mBlockJump=0;
pushStack(Data::pointer(new BoolData(mBlockSuccess)));
return(true);
}
TER Interpreter::interpret(Contract* contract,const SerializedTransaction& txn,std::vector<unsigned char>& code)
{
mContract=contract;
mCode=&code;
mTotalFee=0;
mInstructionPointer=0;
while(mInstructionPointer<code.size())
{
unsigned int fun=(*mCode)[mInstructionPointer];
mInstructionPointer++;
if(fun>=mFunctionTable.size())
{
// TODO: log
return(temMALFORMED); // TODO: is this actually what we want to do?
}
mTotalFee += mFunctionTable[ fun ]->getFee();
if(mTotalFee>txn.getTransactionFee().getNValue())
{
// TODO: log
return(telINSUF_FEE_P);
}else
{
if(!mFunctionTable[ fun ]->work(this))
{
// TODO: log
return(temMALFORMED); // TODO: is this actually what we want to do?
}
}
}
return(tesSUCCESS);
}
Data::pointer Interpreter::getIntData()
{
int value=0; // TODO
mInstructionPointer += 4;
return(Data::pointer(new IntData(value)));
}
Data::pointer Interpreter::getFloatData()
{
float value=0; // TODO
mInstructionPointer += 4;
return(Data::pointer(new FloatData(value)));
}
Data::pointer Interpreter::getUint160Data()
{
uint160 value; // TODO
mInstructionPointer += 20;
return(Data::pointer(new Uint160Data(value)));
}
bool Interpreter::jumpTo(int offset)
{
mInstructionPointer += offset;
if( (mInstructionPointer<0) || (mInstructionPointer>mCode->size()) )
{
mInstructionPointer -= offset;
return(false);
}
return(true);
}
void Interpreter::stop()
{
mInstructionPointer=mCode->size();
}
Data::pointer Interpreter::getContractData(int index)
{
return(Data::pointer(new ErrorData()));
}
bool Interpreter::canSign(const uint160& signer)
{
return(true);
}
} // end namespace

View File

@@ -0,0 +1,82 @@
#ifndef __INTERPRETER__
#define __INTERPRETER__
#include "uint256.h"
#include "Contract.h"
#include <boost/shared_ptr.hpp>
#include <vector>
#include "ScriptData.h"
#include "TransactionEngine.h"
namespace Script {
class Operation;
// Contracts are non typed have variable data types
class Interpreter
{
std::vector<Operation*> mFunctionTable;
std::vector<Data::pointer> mStack;
Contract* mContract;
std::vector<unsigned char>* mCode;
unsigned int mInstructionPointer;
int mTotalFee;
bool mInBlock;
int mBlockJump;
bool mBlockSuccess;
public:
enum { INT_OP=1,FLOAT_OP,UINT160_OP,BOOL_OP,PATH_OP,
ADD_OP,SUB_OP,MUL_OP,DIV_OP,MOD_OP,
GTR_OP,LESS_OP,EQUAL_OP,NOT_EQUAL_OP,
AND_OP,OR_OP,NOT_OP,
JUMP_OP, JUMPIF_OP,
STOP_OP, CANCEL_OP,
BLOCK_OP, BLOCK_END_OP,
SEND_XRP_OP,SEND_OP,REMOVE_CONTRACT_OP,FEE_OP,CHANGE_CONTRACT_OWNER_OP,
STOP_REMOVE_OP,
SET_DATA_OP,GET_DATA_OP, GET_NUM_DATA_OP,
SET_REGISTER_OP,GET_REGISTER_OP,
GET_ISSUER_ID_OP, GET_OWNER_ID_OP, GET_LEDGER_TIME_OP, GET_LEDGER_NUM_OP, GET_RAND_FLOAT_OP,
GET_XRP_ESCROWED_OP, GET_RIPPLE_ESCROWED_OP, GET_RIPPLE_ESCROWED_CURRENCY_OP, GET_RIPPLE_ESCROWED_ISSUER,
GET_ACCEPT_DATA_OP, GET_ACCEPTOR_ID_OP, GET_CONTRACT_ID_OP,
NUM_OF_OPS };
Interpreter();
// returns a TransactionEngineResult
TER interpret(Contract* contract,const SerializedTransaction& txn,std::vector<unsigned char>& code);
void stop();
bool canSign(const uint160& signer);
int getInstructionPointer(){ return(mInstructionPointer); }
void setInstructionPointer(int n){ mInstructionPointer=n;}
Data::pointer popStack();
void pushStack(Data::pointer data);
bool jumpTo(int offset);
bool startBlock(int offset);
bool endBlock();
Data::pointer getIntData();
Data::pointer getFloatData();
Data::pointer getUint160Data();
Data::pointer getAcceptData(int index);
Data::pointer getContractData(int index);
};
} // end namespace
#endif

181
src/cpp/ripple/JobQueue.cpp Normal file
View File

@@ -0,0 +1,181 @@
#include "JobQueue.h"
#include <boost/make_shared.hpp>
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include "Log.h"
SETUP_LOG();
const char* Job::toString(JobType t)
{
switch(t)
{
case jtINVALID: return "invalid";
case jtVALIDATION_ut: return "untrustedValidation";
case jtTRANSACTION: return "transaction";
case jtPROPOSAL_ut: return "untrustedProposal";
case jtVALIDATION_t: return "trustedValidation";
case jtPROPOSAL_t: return "trustedProposal";
case jtADMIN: return "administration";
case jtDEATH: return "jobOfDeath";
default: assert(false); return "unknown";
}
}
bool Job::operator<(const Job& j) const
{ // These comparison operators make the jobs sort in priority order in the job set
if (mType < j.mType)
return true;
if (mType > j.mType)
return false;
return mJobIndex < j.mJobIndex;
}
bool Job::operator<=(const Job& j) const
{
if (mType < j.mType)
return true;
if (mType > j.mType)
return false;
return mJobIndex <= j.mJobIndex;
}
bool Job::operator>(const Job& j) const
{
if (mType < j.mType)
return false;
if (mType > j.mType)
return true;
return mJobIndex > j.mJobIndex;
}
bool Job::operator>=(const Job& j) const
{
if (mType < j.mType)
return false;
if (mType > j.mType)
return true;
return mJobIndex >= j.mJobIndex;
}
void JobQueue::addJob(JobType type, const boost::function<void(Job&)>& jobFunc)
{
assert(type != jtINVALID);
boost::mutex::scoped_lock sl(mJobLock);
assert(mThreadCount != 0); // do not add jobs to a queue with no threads
mJobSet.insert(Job(type, ++mLastJob, jobFunc));
++mJobCounts[type];
mJobCond.notify_one();
}
int JobQueue::getJobCount(JobType t)
{
boost::mutex::scoped_lock sl(mJobLock);
std::map<JobType, int>::iterator c = mJobCounts.find(t);
return (c == mJobCounts.end()) ? 0 : c->second;
}
int JobQueue::getJobCountGE(JobType t)
{ // return the number of jobs at this priority level or greater
int ret = 0;
boost::mutex::scoped_lock sl(mJobLock);
typedef std::pair<JobType, int> jt_int_pair;
BOOST_FOREACH(const jt_int_pair& it, mJobCounts)
if (it.first >= t)
ret += it.second;
return ret;
}
std::vector< std::pair<JobType, int> > JobQueue::getJobCounts()
{ // return all jobs at all priority levels
std::vector< std::pair<JobType, int> > ret;
boost::mutex::scoped_lock sl(mJobLock);
ret.reserve(mJobCounts.size());
typedef std::pair<JobType, int> jt_int_pair;
BOOST_FOREACH(const jt_int_pair& it, mJobCounts)
ret.push_back(it);
return ret;
}
void JobQueue::shutdown()
{ // shut down the job queue without completing pending jobs
cLog(lsINFO) << "Job queue shutting down";
boost::mutex::scoped_lock sl(mJobLock);
mShuttingDown = true;
mJobCond.notify_all();
while (mThreadCount != 0)
mJobCond.wait(sl);
}
void JobQueue::setThreadCount(int c)
{ // set the number of thread serving the job queue to precisely this number
if (c == 0)
{
c = boost::thread::hardware_concurrency();
if (c < 2)
c = 2;
cLog(lsINFO) << "Auto-tuning to " << c << " validation/transaction/proposal threads";
}
boost::mutex::scoped_lock sl(mJobLock);
while (mJobCounts[jtDEATH] != 0)
mJobCond.wait(sl);
while (mThreadCount < c)
{
++mThreadCount;
boost::thread t(boost::bind(&JobQueue::threadEntry, this));
t.detach();
}
while (mThreadCount > c)
{
if (mJobCounts[jtDEATH] != 0)
mJobCond.wait(sl);
else
{
mJobSet.insert(Job(jtDEATH, 0));
++mJobCounts[jtDEATH];
}
}
mJobCond.notify_one(); // in case we sucked up someone else's signal
}
void JobQueue::threadEntry()
{ // do jobs until asked to stop
boost::mutex::scoped_lock sl(mJobLock);
while (1)
{
while (mJobSet.empty() && !mShuttingDown)
mJobCond.wait(sl);
if (mShuttingDown)
break;
std::set<Job>::iterator it = mJobSet.begin();
Job job(*it);
mJobSet.erase(it);
--mJobCounts[job.getType()];
if (job.getType() == jtDEATH)
break;
sl.unlock();
cLog(lsDEBUG) << "Doing " << Job::toString(job.getType()) << " job";
job.doJob();
sl.lock();
}
--mThreadCount;
mJobCond.notify_all();
}

83
src/cpp/ripple/JobQueue.h Normal file
View File

@@ -0,0 +1,83 @@
#ifndef JOB_QUEUE__H
#define JOB_QUEUE__H
#include <map>
#include <set>
#include <vector>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/function.hpp>
#include "types.h"
// Note that this queue should only be used for CPU-bound jobs
// It is primarily intended for signature checking
enum JobType
{ // must be in priority order, low to high
jtINVALID,
jtVALIDATION_ut,
jtTRANSACTION,
jtPROPOSAL_ut,
jtVALIDATION_t,
jtPROPOSAL_t,
jtADMIN,
jtDEATH, // job of death, used internally
};
class Job
{
protected:
JobType mType;
uint64 mJobIndex;
boost::function<void(Job&)> mJob;
public:
Job() : mType(jtINVALID), mJobIndex(0) { ; }
Job(JobType type, uint64 index) : mType(type), mJobIndex(index) { ; }
Job(JobType type, uint64 index, const boost::function<void(Job&)>& job)
: mType(type), mJobIndex(index), mJob(job) { ; }
JobType getType() const { return mType; }
void doJob(void) { mJob(*this); }
bool operator<(const Job& j) const;
bool operator>(const Job& j) const;
bool operator<=(const Job& j) const;
bool operator>=(const Job& j) const;
static const char* toString(JobType);
};
class JobQueue
{
protected:
boost::mutex mJobLock;
boost::condition_variable mJobCond;
uint64 mLastJob;
std::set<Job> mJobSet;
std::map<JobType, int> mJobCounts;
int mThreadCount;
bool mShuttingDown;
void threadEntry(void);
public:
JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false) { ; }
void addJob(JobType type, const boost::function<void(Job&)>& job);
int getJobCount(JobType t); // Jobs at this priority
int getJobCountGE(JobType t); // All jobs at or greater than this priority
std::vector< std::pair<JobType, int> > getJobCounts();
void shutdown();
void setThreadCount(int c = 0);
};
#endif

1093
src/cpp/ripple/Ledger.cpp Normal file

File diff suppressed because it is too large Load Diff

315
src/cpp/ripple/Ledger.h Normal file
View File

@@ -0,0 +1,315 @@
#ifndef __LEDGER__
#define __LEDGER__
#include <map>
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "../json/value.h"
#include "Transaction.h"
#include "TransactionMeta.h"
#include "AccountState.h"
#include "RippleState.h"
#include "NicknameState.h"
#include "types.h"
#include "BitcoinUtil.h"
#include "SHAMap.h"
#include "InstanceCounter.h"
enum LedgerStateParms
{
lepNONE = 0, // no special flags
// input flags
lepCREATE = 1, // Create if not present
// output flags
lepOKAY = 2, // success
lepMISSING = 4, // No node in that slot
lepWRONGTYPE = 8, // Node of different type there
lepCREATED = 16, // Node was created
lepERROR = 32, // error
};
#define LEDGER_JSON_DUMP_TXRP 0x10000000
#define LEDGER_JSON_DUMP_STATE 0x20000000
#define LEDGER_JSON_FULL 0x40000000
DEFINE_INSTANCE(Ledger);
class Ledger : public boost::enable_shared_from_this<Ledger>, public IS_INSTANCE(Ledger)
{ // The basic Ledger structure, can be opened, closed, or synching
friend class TransactionEngine;
public:
typedef boost::shared_ptr<Ledger> pointer;
typedef const boost::shared_ptr<Ledger>& ref;
enum TransResult
{
TR_ERROR = -1,
TR_SUCCESS = 0,
TR_NOTFOUND = 1,
TR_ALREADY = 2,
TR_BADTRANS = 3, // the transaction itself is corrupt
TR_BADACCT = 4, // one of the accounts is invalid
TR_INSUFF = 5, // the sending(apply)/receiving(remove) account is broke
TR_PASTASEQ = 6, // account is past this transaction
TR_PREASEQ = 7, // account is missing transactions before this
TR_BADLSEQ = 8, // ledger too early
TR_TOOSMALL = 9, // amount is less than Tx fee
};
// ledger close flags
static const uint32 sLCF_NoConsensusTime = 1;
private:
uint256 mHash, mParentHash, mTransHash, mAccountHash;
uint64 mTotCoins;
uint32 mLedgerSeq;
uint32 mCloseTime; // when this ledger closed
uint32 mParentCloseTime; // when the previous ledger closed
int mCloseResolution; // the resolution for this ledger close time (2-120 seconds)
uint32 mCloseFlags; // flags indicating how this ledger close took place
bool mClosed, mValidHash, mAccepted, mImmutable;
SHAMap::pointer mTransactionMap, mAccountStateMap;
mutable boost::recursive_mutex mLock;
static int sPendingSaves;
static boost::recursive_mutex sPendingSaveLock;
Ledger(const Ledger&); // no implementation
Ledger& operator=(const Ledger&); // no implementation
protected:
SLE::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID, LedgerEntryType let);
static void incPendingSaves();
static void decPendingSaves();
void saveAcceptedLedger(bool fromConsensus);
public:
Ledger(const RippleAddress& masterID, uint64 startAmount); // used for the starting bootstrap ledger
Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash,
uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution,
uint32 ledgerSeq); // used for database ledgers
Ledger(const std::vector<unsigned char>& rawLedger);
Ledger(const std::string& rawLedger);
Ledger(bool dummy, Ledger& previous); // ledger after this one
Ledger(Ledger& target, bool isMutable); // snapshot
static Ledger::pointer getSQL(const std::string& sqlStatement);
static Ledger::pointer getLastFullLedger();
static int getPendingSaves();
void updateHash();
void setClosed() { mClosed = true; }
void setAccepted(uint32 closeTime, int closeResolution, bool correctCloseTime);
void setAccepted();
void setImmutable() { updateHash(); mImmutable = true; }
bool isClosed() { return mClosed; }
bool isAccepted() { return mAccepted; }
bool isImmutable() { return mImmutable; }
// ledger signature operations
void addRaw(Serializer &s) const;
void setRaw(Serializer& s);
uint256 getHash();
const uint256& getParentHash() const { return mParentHash; }
const uint256& getTransHash() const { return mTransHash; }
const uint256& getAccountHash() const { return mAccountHash; }
uint64 getTotalCoins() const { return mTotCoins; }
void destroyCoins(uint64 fee) { mTotCoins -= fee; }
uint32 getCloseTimeNC() const { return mCloseTime; }
uint32 getParentCloseTimeNC() const { return mParentCloseTime; }
uint32 getLedgerSeq() const { return mLedgerSeq; }
int getCloseResolution() const { return mCloseResolution; }
bool getCloseAgree() const { return (mCloseFlags & sLCF_NoConsensusTime) == 0; }
// close time functions
void setCloseTime(uint32 ct) { assert(!mImmutable); mCloseTime = ct; }
void setCloseTime(boost::posix_time::ptime);
boost::posix_time::ptime getCloseTime() const;
// low level functions
SHAMap::ref peekTransactionMap() { return mTransactionMap; }
SHAMap::ref peekAccountStateMap() { return mAccountStateMap; }
// ledger sync functions
void setAcquiring(void);
bool isAcquiring(void);
bool isAcquiringTx(void);
bool isAcquiringAS(void);
// Transaction Functions
bool addTransaction(const uint256& id, const Serializer& txn);
bool addTransaction(const uint256& id, const Serializer& txn, const Serializer& metaData);
bool hasTransaction(const uint256& TransID) const { return mTransactionMap->hasItem(TransID); }
Transaction::pointer getTransaction(const uint256& transID) const;
bool getTransaction(const uint256& transID, Transaction::pointer& txn, TransactionMetaSet::pointer& txMeta);
static SerializedTransaction::pointer getSTransaction(SHAMapItem::ref, SHAMapTreeNode::TNType);
// high-level functions
AccountState::pointer getAccountState(const RippleAddress& acctID);
LedgerStateParms writeBack(LedgerStateParms parms, SLE::ref);
SLE::pointer getAccountRoot(const uint160& accountID);
SLE::pointer getAccountRoot(const RippleAddress& naAccountID);
// database functions
static Ledger::pointer loadByIndex(uint32 ledgerIndex);
static Ledger::pointer loadByHash(const uint256& ledgerHash);
void pendSave(bool fromConsensus);
// next/prev function
SLE::pointer getSLE(const uint256& uHash);
uint256 getFirstLedgerIndex();
uint256 getLastLedgerIndex();
uint256 getNextLedgerIndex(const uint256& uHash); // first node >hash
uint256 getNextLedgerIndex(const uint256& uHash, const uint256& uEnd); // first node >hash, <end
uint256 getPrevLedgerIndex(const uint256& uHash); // last node <hash
uint256 getPrevLedgerIndex(const uint256& uHash, const uint256& uBegin); // last node <hash, >begin
// Ledger hash table function
static uint256 getLedgerHashIndex();
static uint256 getLedgerHashIndex(uint32 desiredLedgerIndex);
static int getLedgerHashOffset(uint32 desiredLedgerIndex);
static int getLedgerHashOffset(uint32 desiredLedgerIndex, uint32 currentLedgerIndex);
// index calculation functions
static uint256 getAccountRootIndex(const uint160& uAccountID);
static uint256 getAccountRootIndex(const RippleAddress& account)
{ return getAccountRootIndex(account.getAccountID()); }
//
// Generator Map functions
//
SLE::pointer getGenerator(LedgerStateParms& parms, const uint160& uGeneratorID);
static uint256 getGeneratorIndex(const uint160& uGeneratorID);
//
// Nickname functions
//
static uint256 getNicknameHash(const std::string& strNickname)
{ Serializer s(strNickname); return s.getSHA256(); }
NicknameState::pointer getNicknameState(const uint256& uNickname);
NicknameState::pointer getNicknameState(const std::string& strNickname)
{ return getNicknameState(getNicknameHash(strNickname)); }
SLE::pointer getNickname(LedgerStateParms& parms, const uint256& uNickname);
SLE::pointer getNickname(LedgerStateParms& parms, const std::string& strNickname)
{ return getNickname(parms, getNicknameHash(strNickname)); }
static uint256 getNicknameIndex(const uint256& uNickname);
//
// Order book functions
//
// Order book dirs have a base so we can use next to step through them in quality order.
static uint256 getBookBase(const uint160& uTakerPaysCurrency, const uint160& uTakerPaysIssuerID,
const uint160& uTakerGetsCurrency, const uint160& uTakerGetsIssuerID);
//
// Offer functions
//
SLE::pointer getOffer(LedgerStateParms& parms, const uint256& uIndex);
SLE::pointer getOffer(const uint256& uIndex)
{
LedgerStateParms qry = lepNONE;
return getOffer(qry, uIndex);
}
SLE::pointer getOffer(LedgerStateParms& parms, const uint160& uAccountID, uint32 uSequence)
{ return getOffer(parms, getOfferIndex(uAccountID, uSequence)); }
// The index of an offer.
static uint256 getOfferIndex(const uint160& uAccountID, uint32 uSequence);
//
// Owner functions
//
// All items controlled by an account are here: offers
static uint256 getOwnerDirIndex(const uint160& uAccountID);
//
// Directory functions
// Directories are doubly linked lists of nodes.
// Given a directory root and and index compute the index of a node.
static uint256 getDirNodeIndex(const uint256& uDirRoot, const uint64 uNodeIndex=0);
// Return a node: root or normal
SLE::pointer getDirNode(LedgerStateParms& parms, const uint256& uNodeIndex);
//
// Quality
//
static uint256 getQualityIndex(const uint256& uBase, const uint64 uNodeDir=0);
static uint256 getQualityNext(const uint256& uBase);
static uint64 getQuality(const uint256& uBase);
//
// Ripple functions : credit lines
//
// Index of node which is the ripple state between two accounts for a currency.
static uint256 getRippleStateIndex(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency);
static uint256 getRippleStateIndex(const uint160& uiA, const uint160& uiB, const uint160& uCurrency)
{ return getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency); }
RippleState::pointer accessRippleState(const uint256& uNode);
SLE::pointer getRippleState(LedgerStateParms& parms, const uint256& uNode);
SLE::pointer getRippleState(const uint256& uNode)
{
LedgerStateParms qry = lepNONE;
return getRippleState(qry, uNode);
}
SLE::pointer getRippleState(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency)
{ return getRippleState(getRippleStateIndex(naA, naB, uCurrency)); }
SLE::pointer getRippleState(const uint160& uiA, const uint160& uiB, const uint160& uCurrency)
{ return getRippleState(getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency)); }
Json::Value getJson(int options);
void addJson(Json::Value&, int options);
bool walkLedger();
bool assertSane();
};
inline LedgerStateParms operator|(const LedgerStateParms& l1, const LedgerStateParms& l2)
{
return static_cast<LedgerStateParms>(static_cast<int>(l1) | static_cast<int>(l2));
}
inline LedgerStateParms operator&(const LedgerStateParms& l1, const LedgerStateParms& l2)
{
return static_cast<LedgerStateParms>(static_cast<int>(l1) & static_cast<int>(l2));
}
#endif
// vim:ts=4

View File

@@ -0,0 +1,615 @@
#include "LedgerAcquire.h"
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <boost/bind.hpp>
#include "Application.h"
#include "Log.h"
#include "SHAMapSync.h"
#include "HashPrefixes.h"
SETUP_LOG();
DECLARE_INSTANCE(PeerSet);
#define LA_DEBUG
#define LEDGER_ACQUIRE_TIMEOUT 750
#define TRUST_NETWORK
PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterval(interval), mTimeouts(0),
mComplete(false), mFailed(false), mProgress(true), mTimer(theApp->getIOService())
{
assert((mTimerInterval > 10) && (mTimerInterval < 30000));
}
void PeerSet::peerHas(Peer::ref ptr)
{
boost::recursive_mutex::scoped_lock sl(mLock);
if (!mPeers.insert(std::make_pair(ptr->getPeerId(), 0)).second)
return;
newPeer(ptr);
}
void PeerSet::badPeer(Peer::ref ptr)
{
boost::recursive_mutex::scoped_lock sl(mLock);
mPeers.erase(ptr->getPeerId());
}
void PeerSet::resetTimer()
{
mTimer.expires_from_now(boost::posix_time::milliseconds(mTimerInterval));
mTimer.async_wait(boost::bind(&PeerSet::TimerEntry, pmDowncast(), boost::asio::placeholders::error));
}
void PeerSet::invokeOnTimer()
{
if (!mProgress)
{
++mTimeouts;
cLog(lsWARNING) << "Timeout(" << mTimeouts << ") pc=" << mPeers.size() << " acquiring " << mHash;
onTimer(false);
}
else
{
mProgress = false;
onTimer(true);
}
}
void PeerSet::TimerEntry(boost::weak_ptr<PeerSet> wptr, const boost::system::error_code& result)
{
if (result == boost::asio::error::operation_aborted)
return;
boost::shared_ptr<PeerSet> ptr = wptr.lock();
if (ptr)
ptr->invokeOnTimer();
}
LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT),
mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false), mAccept(false)
{
#ifdef LA_DEBUG
cLog(lsTRACE) << "Acquiring ledger " << mHash;
#endif
}
bool LedgerAcquire::tryLocal()
{ // return value: true = no more work to do
HashedObject::pointer node = theApp->getHashedObjectStore().retrieve(mHash);
if (!node)
return false;
mLedger = boost::make_shared<Ledger>(strCopy(node->getData()));
assert(mLedger->getHash() == mHash);
mHaveBase = true;
if (!mLedger->getTransHash())
mHaveTransactions = true;
else
{
try
{
mLedger->peekTransactionMap()->fetchRoot(mLedger->getTransHash());
}
catch (SHAMapMissingNode&)
{
}
}
if (!mLedger->getAccountHash())
mHaveState = true;
else
{
try
{
mLedger->peekAccountStateMap()->fetchRoot(mLedger->getAccountHash());
}
catch (SHAMapMissingNode&)
{
}
}
return mHaveTransactions && mHaveState;
}
void LedgerAcquire::onTimer(bool progress)
{
if (getTimeouts() > 6)
{
setFailed();
done();
}
else if (!progress)
{
if (!getPeerCount())
addPeers();
else
trigger(Peer::pointer(), true);
}
}
void LedgerAcquire::addPeers()
{
std::vector<Peer::pointer> peerList = theApp->getConnectionPool().getPeerVector();
bool found = false;
BOOST_FOREACH(Peer::ref peer, peerList)
{
if (peer->hasLedger(getHash()))
{
found = true;
peerHas(peer);
}
}
if (!found)
{
BOOST_FOREACH(Peer::ref peer, peerList)
peerHas(peer);
}
}
boost::weak_ptr<PeerSet> LedgerAcquire::pmDowncast()
{
return boost::shared_polymorphic_downcast<PeerSet>(shared_from_this());
}
void LedgerAcquire::done()
{
if (mSignaled)
return;
mSignaled = true;
#ifdef LA_DEBUG
cLog(lsTRACE) << "Done acquiring ledger " << mHash;
#endif
std::vector< boost::function<void (LedgerAcquire::pointer)> > triggers;
setComplete();
mLock.lock();
triggers = mOnComplete;
mOnComplete.clear();
mLock.unlock();
if (mLedger)
{
if (mAccept)
mLedger->setAccepted();
theApp->getMasterLedger().storeLedger(mLedger);
}
for (unsigned int i = 0; i < triggers.size(); ++i)
triggers[i](shared_from_this());
}
void LedgerAcquire::addOnComplete(boost::function<void (LedgerAcquire::pointer)> trigger)
{
mLock.lock();
mOnComplete.push_back(trigger);
mLock.unlock();
}
void LedgerAcquire::trigger(Peer::ref peer, bool timer)
{
if (mAborted || mComplete || mFailed)
{
cLog(lsTRACE) << "Trigger on ledger:" <<
(mAborted ? " aborted": "") << (mComplete ? " completed": "") << (mFailed ? " failed" : "");
return;
}
if (sLog(lsTRACE))
{
if (peer)
cLog(lsTRACE) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP();
else
cLog(lsTRACE) << "Trigger acquiring ledger " << mHash;
if (mComplete || mFailed)
cLog(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed;
else
cLog(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState;
}
if (!mHaveBase)
{
ripple::TMGetLedger tmGL;
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
tmGL.set_itype(ripple::liBASE);
cLog(lsTRACE) << "Sending base request to " << (peer ? "selected peer" : "all peers");
sendRequest(tmGL, peer);
}
if (mHaveBase && !mHaveTransactions)
{
assert(mLedger);
if (mLedger->peekTransactionMap()->getHash().isZero())
{ // we need the root node
ripple::TMGetLedger tmGL;
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
tmGL.set_itype(ripple::liTX_NODE);
*(tmGL.add_nodeids()) = SHAMapNode().getRawString();
cLog(lsTRACE) << "Sending TX root request to " << (peer ? "selected peer" : "all peers");
sendRequest(tmGL, peer);
}
else
{
std::vector<SHAMapNode> nodeIDs;
std::vector<uint256> nodeHashes;
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
mLedger->peekTransactionMap()->getMissingNodes(nodeIDs, nodeHashes, 128, &tFilter);
if (nodeIDs.empty())
{
if (!mLedger->peekTransactionMap()->isValid()) mFailed = true;
else
{
mHaveTransactions = true;
if (mHaveState)
mComplete = true;
}
}
else
{
ripple::TMGetLedger tmGL;
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
tmGL.set_itype(ripple::liTX_NODE);
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
*(tmGL.add_nodeids()) = it.getRawString();
cLog(lsTRACE) << "Sending TX node " << nodeIDs.size()
<< " request to " << (peer ? "selected peer" : "all peers");
sendRequest(tmGL, peer);
}
}
}
if (mHaveBase && !mHaveState)
{
assert(mLedger);
if (mLedger->peekAccountStateMap()->getHash().isZero())
{ // we need the root node
ripple::TMGetLedger tmGL;
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
tmGL.set_itype(ripple::liAS_NODE);
*(tmGL.add_nodeids()) = SHAMapNode().getRawString();
cLog(lsTRACE) << "Sending AS root request to " << (peer ? "selected peer" : "all peers");
sendRequest(tmGL, peer);
}
else
{
std::vector<SHAMapNode> nodeIDs;
std::vector<uint256> nodeHashes;
AccountStateSF aFilter(mLedger->getHash(), mLedger->getLedgerSeq());
mLedger->peekAccountStateMap()->getMissingNodes(nodeIDs, nodeHashes, 128, &aFilter);
if (nodeIDs.empty())
{
if (!mLedger->peekAccountStateMap()->isValid()) mFailed = true;
else
{
mHaveState = true;
if (mHaveTransactions)
mComplete = true;
}
}
else
{
ripple::TMGetLedger tmGL;
tmGL.set_ledgerhash(mHash.begin(), mHash.size());
tmGL.set_ledgerseq(mLedger->getLedgerSeq());
tmGL.set_itype(ripple::liAS_NODE);
BOOST_FOREACH(SHAMapNode& it, nodeIDs)
*(tmGL.add_nodeids()) = it.getRawString();
cLog(lsTRACE) << "Sending AS node " << nodeIDs.size()
<< " request to " << (peer ? "selected peer" : "all peers");
tLog(nodeIDs.size() == 1, lsTRACE) << "AS node: " << nodeIDs[0];
sendRequest(tmGL, peer);
}
}
}
if (mComplete || mFailed)
{
cLog(lsDEBUG) << "Done:" << (mComplete ? " complete" : "") << (mFailed ? " failed" : "");
done();
}
else if (timer)
resetTimer();
}
void PeerSet::sendRequest(const ripple::TMGetLedger& tmGL, Peer::ref peer)
{
if (!peer)
sendRequest(tmGL);
else
peer->sendPacket(boost::make_shared<PackedMessage>(tmGL, ripple::mtGET_LEDGER));
}
void PeerSet::sendRequest(const ripple::TMGetLedger& tmGL)
{
boost::recursive_mutex::scoped_lock sl(mLock);
if (mPeers.empty())
return;
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tmGL, ripple::mtGET_LEDGER);
for (boost::unordered_map<uint64, int>::iterator it = mPeers.begin(), end = mPeers.end(); it != end; ++it)
{
Peer::pointer peer = theApp->getConnectionPool().getPeerById(it->first);
if (peer)
peer->sendPacket(packet);
}
}
int PeerSet::takePeerSetFrom(const PeerSet& s)
{
int ret = 0;
mPeers.clear();
for (boost::unordered_map<uint64, int>::const_iterator it = s.mPeers.begin(), end = s.mPeers.end();
it != end; ++it)
{
mPeers.insert(std::make_pair(it->first, 0));
++ret;
}
return ret;
}
int PeerSet::getPeerCount() const
{
int ret = 0;
for (boost::unordered_map<uint64, int>::const_iterator it = mPeers.begin(), end = mPeers.end(); it != end; ++it)
if (theApp->getConnectionPool().hasPeer(it->first))
++ret;
return ret;
}
bool LedgerAcquire::takeBase(const std::string& data)
{ // Return value: true=normal, false=bad data
#ifdef LA_DEBUG
cLog(lsTRACE) << "got base acquiring ledger " << mHash;
#endif
boost::recursive_mutex::scoped_lock sl(mLock);
if (mHaveBase) return true;
mLedger = boost::make_shared<Ledger>(data);
if (mLedger->getHash() != mHash)
{
cLog(lsWARNING) << "Acquire hash mismatch";
cLog(lsWARNING) << mLedger->getHash() << "!=" << mHash;
mLedger.reset();
#ifdef TRUST_NETWORK
assert(false);
#endif
return false;
}
mHaveBase = true;
Serializer s(data.size() + 4);
s.add32(sHP_Ledger);
s.addRaw(data);
theApp->getHashedObjectStore().store(hotLEDGER, mLedger->getLedgerSeq(), s.peekData(), mHash);
progress();
if (!mLedger->getTransHash())
mHaveTransactions = true;
if (!mLedger->getAccountHash())
mHaveState = true;
mLedger->setAcquiring();
return true;
}
bool LedgerAcquire::takeTxNode(const std::list<SHAMapNode>& nodeIDs,
const std::list< std::vector<unsigned char> >& data)
{
if (!mHaveBase) return false;
std::list<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin();
std::list< std::vector<unsigned char> >::const_iterator nodeDatait = data.begin();
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
while (nodeIDit != nodeIDs.end())
{
if (nodeIDit->isRoot())
{
if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), *nodeDatait, snfWIRE, &tFilter))
return false;
}
else if (!mLedger->peekTransactionMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter))
return false;
++nodeIDit;
++nodeDatait;
}
if (!mLedger->peekTransactionMap()->isSynching())
{
mHaveTransactions = true;
if (mHaveState)
{
mComplete = true;
done();
}
}
progress();
return true;
}
bool LedgerAcquire::takeAsNode(const std::list<SHAMapNode>& nodeIDs,
const std::list< std::vector<unsigned char> >& data)
{
cLog(lsTRACE) << "got ASdata (" << nodeIDs.size() <<") acquiring ledger " << mHash;
tLog(nodeIDs.size() == 1, lsTRACE) << "got AS node: " << nodeIDs.front();
if (!mHaveBase)
{
cLog(lsWARNING) << "Don't have ledger base";
return false;
}
std::list<SHAMapNode>::const_iterator nodeIDit = nodeIDs.begin();
std::list< std::vector<unsigned char> >::const_iterator nodeDatait = data.begin();
AccountStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
while (nodeIDit != nodeIDs.end())
{
if (nodeIDit->isRoot())
{
if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(),
*nodeDatait, snfWIRE, &tFilter))
{
cLog(lsWARNING) << "Bad ledger base";
return false;
}
}
else if (!mLedger->peekAccountStateMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter))
{
cLog(lsWARNING) << "Unable to add AS node";
return false;
}
++nodeIDit;
++nodeDatait;
}
if (!mLedger->peekAccountStateMap()->isSynching())
{
mHaveState = true;
if (mHaveTransactions)
{
mComplete = true;
done();
}
}
progress();
return true;
}
bool LedgerAcquire::takeAsRootNode(const std::vector<unsigned char>& data)
{
if (!mHaveBase)
return false;
AccountStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), data, snfWIRE, &tFilter))
return false;
return true;
}
bool LedgerAcquire::takeTxRootNode(const std::vector<unsigned char>& data)
{
if (!mHaveBase)
return false;
TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq());
if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), data, snfWIRE, &tFilter))
return false;
return true;
}
LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash)
{
assert(hash.isNonZero());
boost::mutex::scoped_lock sl(mLock);
LedgerAcquire::pointer& ptr = mLedgers[hash];
if (ptr)
return ptr;
ptr = boost::make_shared<LedgerAcquire>(hash);
assert(mLedgers[hash] == ptr);
ptr->addPeers();
ptr->resetTimer(); // Cannot call in constructor
return ptr;
}
LedgerAcquire::pointer LedgerAcquireMaster::find(const uint256& hash)
{
assert(hash.isNonZero());
boost::mutex::scoped_lock sl(mLock);
std::map<uint256, LedgerAcquire::pointer>::iterator it = mLedgers.find(hash);
if (it != mLedgers.end())
return it->second;
return LedgerAcquire::pointer();
}
bool LedgerAcquireMaster::hasLedger(const uint256& hash)
{
assert(hash.isNonZero());
boost::mutex::scoped_lock sl(mLock);
return mLedgers.find(hash) != mLedgers.end();
}
void LedgerAcquireMaster::dropLedger(const uint256& hash)
{
assert(hash.isNonZero());
boost::mutex::scoped_lock sl(mLock);
mLedgers.erase(hash);
}
bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref peer)
{
uint256 hash;
if (packet.ledgerhash().size() != 32)
{
std::cerr << "Acquire error" << std::endl;
return false;
}
memcpy(hash.begin(), packet.ledgerhash().data(), 32);
cLog(lsTRACE) << "Got data ( " << packet.nodes().size() << ") for acquiring ledger: " << hash;
LedgerAcquire::pointer ledger = find(hash);
if (!ledger)
{
cLog(lsINFO) << "Got data for ledger we're not acquiring";
return false;
}
if (packet.type() == ripple::liBASE)
{
if (packet.nodes_size() < 1)
{
cLog(lsWARNING) << "Got empty base data";
return false;
}
if (!ledger->takeBase(packet.nodes(0).nodedata()))
{
cLog(lsWARNING) << "Got invalid base data";
return false;
}
if ((packet.nodes().size() > 1) && !ledger->takeAsRootNode(strCopy(packet.nodes(1).nodedata())))
{
cLog(lsWARNING) << "Included ASbase invalid";
}
if ((packet.nodes().size() > 2) && !ledger->takeTxRootNode(strCopy(packet.nodes(2).nodedata())))
{
cLog(lsWARNING) << "Included TXbase invalid";
}
ledger->trigger(peer, false);
return true;
}
if ((packet.type() == ripple::liTX_NODE) || (packet.type() == ripple::liAS_NODE))
{
std::list<SHAMapNode> nodeIDs;
std::list< std::vector<unsigned char> > nodeData;
if (packet.nodes().size() <= 0)
{
cLog(lsINFO) << "Got request for no nodes";
return false;
}
for (int i = 0; i < packet.nodes().size(); ++i)
{
const ripple::TMLedgerNode& node = packet.nodes(i);
if (!node.has_nodeid() || !node.has_nodedata())
{
cLog(lsWARNING) << "Got bad node";
return false;
}
nodeIDs.push_back(SHAMapNode(node.nodeid().data(), node.nodeid().size()));
nodeData.push_back(std::vector<unsigned char>(node.nodedata().begin(), node.nodedata().end()));
}
bool ret;
if (packet.type() == ripple::liTX_NODE)
ret = ledger->takeTxNode(nodeIDs, nodeData);
else
ret = ledger->takeAsNode(nodeIDs, nodeData);
if (ret)
ledger->trigger(peer, false);
return ret;
}
cLog(lsWARNING) << "Not sure what ledger data we got";
return false;
}
// vim:ts=4

View File

@@ -0,0 +1,127 @@
#ifndef __LEDGERACQUIRE__
#define __LEDGERACQUIRE__
#include <vector>
#include <map>
#include <list>
#include <boost/enable_shared_from_this.hpp>
#include <boost/function.hpp>
#include <boost/asio.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/unordered_map.hpp>
#include <boost/weak_ptr.hpp>
#include "Ledger.h"
#include "Peer.h"
#include "TaggedCache.h"
#include "InstanceCounter.h"
#include "ripple.pb.h"
DEFINE_INSTANCE(PeerSet);
class PeerSet : private IS_INSTANCE(PeerSet)
{
protected:
uint256 mHash;
int mTimerInterval, mTimeouts;
bool mComplete, mFailed, mProgress;
boost::recursive_mutex mLock;
boost::asio::deadline_timer mTimer;
boost::unordered_map<uint64, int> mPeers;
PeerSet(const uint256& hash, int interval);
virtual ~PeerSet() { ; }
void sendRequest(const ripple::TMGetLedger& message);
void sendRequest(const ripple::TMGetLedger& message, Peer::ref peer);
public:
const uint256& getHash() const { return mHash; }
bool isComplete() const { return mComplete; }
bool isFailed() const { return mFailed; }
int getTimeouts() const { return mTimeouts; }
void progress() { mProgress = true; }
void peerHas(Peer::ref);
void badPeer(Peer::ref);
void resetTimer();
int takePeerSetFrom(const PeerSet& s);
int getPeerCount() const;
protected:
virtual void newPeer(Peer::ref) = 0;
virtual void onTimer(bool progress) = 0;
virtual boost::weak_ptr<PeerSet> pmDowncast() = 0;
void setComplete() { mComplete = true; }
void setFailed() { mFailed = true; }
void invokeOnTimer();
private:
static void TimerEntry(boost::weak_ptr<PeerSet>, const boost::system::error_code& result);
};
class LedgerAcquire : public PeerSet, public boost::enable_shared_from_this<LedgerAcquire>
{ // A ledger we are trying to acquire
public:
typedef boost::shared_ptr<LedgerAcquire> pointer;
protected:
Ledger::pointer mLedger;
bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled, mAccept;
std::vector< boost::function<void (LedgerAcquire::pointer)> > mOnComplete;
void done();
void onTimer(bool progress);
void newPeer(Peer::ref peer) { trigger(peer, false); }
boost::weak_ptr<PeerSet> pmDowncast();
public:
LedgerAcquire(const uint256& hash);
virtual ~LedgerAcquire() { ; }
bool isBase() const { return mHaveBase; }
bool isAcctStComplete() const { return mHaveState; }
bool isTransComplete() const { return mHaveTransactions; }
Ledger::pointer getLedger() { return mLedger; }
void abort() { mAborted = true; }
bool setAccept() { if (mAccept) return false; mAccept = true; return true; }
void addOnComplete(boost::function<void (LedgerAcquire::pointer)>);
bool takeBase(const std::string& data);
bool takeTxNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data);
bool takeTxRootNode(const std::vector<unsigned char>& data);
bool takeAsNode(const std::list<SHAMapNode>& IDs, const std::list<std::vector<unsigned char> >& data);
bool takeAsRootNode(const std::vector<unsigned char>& data);
void trigger(Peer::ref, bool timer);
bool tryLocal();
void addPeers();
};
class LedgerAcquireMaster
{
protected:
boost::mutex mLock;
std::map<uint256, LedgerAcquire::pointer> mLedgers;
public:
LedgerAcquireMaster() { ; }
LedgerAcquire::pointer findCreate(const uint256& hash);
LedgerAcquire::pointer find(const uint256& hash);
bool hasLedger(const uint256& ledgerHash);
void dropLedger(const uint256& ledgerHash);
bool gotLedgerData(ripple::TMLedgerData& packet, Peer::ref);
};
#endif
// vim:ts=4

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,195 @@
#ifndef __LEDGER_CONSENSUS__
#define __LEDGER_CONSENSUS__
#include <list>
#include <map>
#include <boost/weak_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/unordered/unordered_map.hpp>
#include "../json/value.h"
#include "key.h"
#include "Transaction.h"
#include "LedgerAcquire.h"
#include "LedgerProposal.h"
#include "Peer.h"
#include "CanonicalTXSet.h"
#include "TransactionEngine.h"
#include "InstanceCounter.h"
DEFINE_INSTANCE(LedgerConsensus);
class TransactionAcquire : public PeerSet, public boost::enable_shared_from_this<TransactionAcquire>
{ // A transaction set we are trying to acquire
public:
typedef boost::shared_ptr<TransactionAcquire> pointer;
protected:
SHAMap::pointer mMap;
bool mHaveRoot;
void onTimer(bool progress);
void newPeer(Peer::ref peer) { trigger(peer, false); }
void done();
void trigger(Peer::ref, bool timer);
boost::weak_ptr<PeerSet> pmDowncast();
public:
TransactionAcquire(const uint256& hash);
virtual ~TransactionAcquire() { ; }
SHAMap::pointer getMap() { return mMap; }
bool takeNodes(const std::list<SHAMapNode>& IDs, const std::list< std::vector<unsigned char> >& data, Peer::ref);
};
class LCTransaction
{ // A transaction that may be disputed
protected:
uint256 mTransactionID;
int mYays, mNays;
bool mOurVote;
Serializer transaction;
boost::unordered_map<uint160, bool> mVotes;
public:
typedef boost::shared_ptr<LCTransaction> pointer;
LCTransaction(const uint256 &txID, const std::vector<unsigned char>& tx, bool ourVote) :
mTransactionID(txID), mYays(0), mNays(0), mOurVote(ourVote), transaction(tx) { ; }
const uint256& getTransactionID() const { return mTransactionID; }
bool getOurVote() const { return mOurVote; }
Serializer& peekTransaction() { return transaction; }
void setOurVote(bool o) { mOurVote = o; }
void setVote(const uint160& peer, bool votesYes);
void unVote(const uint160& peer);
bool updateVote(int percentTime, bool proposing);
};
enum LCState
{
lcsPRE_CLOSE, // We haven't closed our ledger yet, but others might have
lcsESTABLISH, // Establishing consensus
lcsFINISHED, // We have closed on a transaction set
lcsACCEPTED, // We have accepted/validated a new last closed ledger
};
class LedgerConsensus : public boost::enable_shared_from_this<LedgerConsensus>, IS_INSTANCE(LedgerConsensus)
{
protected:
LCState mState;
uint32 mCloseTime; // The wall time this ledger closed
uint256 mPrevLedgerHash, mNewLedgerHash;
Ledger::pointer mPreviousLedger;
LedgerAcquire::pointer mAcquiringLedger;
LedgerProposal::pointer mOurPosition;
RippleAddress mValPublic, mValPrivate;
bool mProposing, mValidating, mHaveCorrectLCL;
int mCurrentMSeconds, mClosePercent, mCloseResolution;
bool mHaveCloseTimeConsensus;
boost::posix_time::ptime mConsensusStartTime;
int mPreviousProposers;
int mPreviousMSeconds;
// Convergence tracking, trusted peers indexed by hash of public key
boost::unordered_map<uint160, LedgerProposal::pointer> mPeerPositions;
// Transaction Sets, indexed by hash of transaction tree
boost::unordered_map<uint256, SHAMap::pointer> mAcquired;
boost::unordered_map<uint256, TransactionAcquire::pointer> mAcquiring;
// Peer sets
boost::unordered_map<uint256, std::vector< boost::weak_ptr<Peer> > > mPeerData;
// Disputed transactions
boost::unordered_map<uint256, LCTransaction::pointer> mDisputes;
// Close time estimates
std::map<uint32, int> mCloseTimes;
// nodes that have bowed out of this consensus process
boost::unordered_set<uint160> mDeadNodes;
// final accept logic
void accept(SHAMap::ref txSet);
void weHave(const uint256& id, Peer::ref avoidPeer);
void startAcquiring(const TransactionAcquire::pointer&);
SHAMap::pointer find(const uint256& hash);
void createDisputes(SHAMap::ref, SHAMap::ref);
void addDisputedTransaction(const uint256&, const std::vector<unsigned char>& transaction);
void adjustCount(SHAMap::ref map, const std::vector<uint160>& peers);
void propose();
void addPosition(LedgerProposal&, bool ours);
void removePosition(LedgerProposal&, bool ours);
void sendHaveTxSet(const uint256& set, bool direct);
void applyTransactions(SHAMap::ref transactionSet, Ledger::ref targetLedger,
Ledger::ref checkLedger, CanonicalTXSet& failedTransactions, bool openLgr);
void applyTransaction(TransactionEngine& engine, SerializedTransaction::ref txn,
Ledger::ref targetLedger, CanonicalTXSet& failedTransactions, bool openLgr);
uint32 roundCloseTime(uint32 closeTime);
// manipulating our own position
void statusChange(ripple::NodeEvent, Ledger& ledger);
void takeInitialPosition(Ledger& initialLedger);
void updateOurPositions();
void playbackProposals();
int getThreshold();
void closeLedger();
void beginAccept(bool synchronous);
void endConsensus();
public:
LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previousLedger, uint32 closeTime);
int startup();
Json::Value getJson();
Ledger::pointer peekPreviousLedger() { return mPreviousLedger; }
uint256 getLCL() { return mPrevLedgerHash; }
SHAMap::pointer getTransactionTree(const uint256& hash, bool doAcquire);
TransactionAcquire::pointer getAcquiring(const uint256& hash);
void mapComplete(const uint256& hash, SHAMap::ref map, bool acquired);
void checkLCL();
void handleLCL(const uint256& lclHash);
void timerEntry();
// state handlers
void statePreClose();
void stateEstablish();
void stateCutoff();
void stateFinished();
void stateAccepted();
bool haveConsensus(bool forReal);
bool peerPosition(const LedgerProposal::pointer&);
bool peerHasSet(Peer::ref peer, const uint256& set, ripple::TxSetStatus status);
bool peerGaveNodes(Peer::ref peer, const uint256& setHash,
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
bool isOurPubKey(const RippleAddress &k) { return k == mValPublic; }
// test/debug
void simulate();
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,150 @@
#ifndef __LEDGERENTRYSET__
#define __LEDGERENTRYSET__
#include <boost/unordered_map.hpp>
#include "SerializedLedger.h"
#include "TransactionMeta.h"
#include "Ledger.h"
#include "TransactionErr.h"
#include "InstanceCounter.h"
DEFINE_INSTANCE(LedgerEntrySetEntry);
DEFINE_INSTANCE(LedgerEntrySet);
enum LedgerEntryAction
{
taaNONE,
taaCACHED, // Unmodified.
taaMODIFY, // Modifed, must have previously been taaCACHED.
taaDELETE, // Delete, must have previously been taaDELETE or taaMODIFY.
taaCREATE, // Newly created.
};
class LedgerEntrySetEntry : private IS_INSTANCE(LedgerEntrySetEntry)
{
public:
SLE::pointer mEntry;
LedgerEntryAction mAction;
int mSeq;
LedgerEntrySetEntry(SLE::ref e, LedgerEntryAction a, int s) : mEntry(e), mAction(a), mSeq(s) { ; }
};
class LedgerEntrySet : private IS_INSTANCE(LedgerEntrySet)
{
protected:
Ledger::pointer mLedger;
std::map<uint256, LedgerEntrySetEntry> mEntries; // cannot be unordered!
TransactionMetaSet mSet;
int mSeq;
LedgerEntrySet(Ledger::ref ledger, const std::map<uint256, LedgerEntrySetEntry> &e,
const TransactionMetaSet& s, int m) : mLedger(ledger), mEntries(e), mSet(s), mSeq(m) { ; }
SLE::pointer getForMod(const uint256& node, Ledger::ref ledger,
boost::unordered_map<uint256, SLE::pointer>& newMods);
bool threadTx(const RippleAddress& threadTo, Ledger::ref ledger,
boost::unordered_map<uint256, SLE::pointer>& newMods);
bool threadTx(SLE::ref threadTo, Ledger::ref ledger, boost::unordered_map<uint256, SLE::pointer>& newMods);
bool threadOwners(SLE::ref node, Ledger::ref ledger, boost::unordered_map<uint256, SLE::pointer>& newMods);
public:
LedgerEntrySet(Ledger::ref ledger) : mLedger(ledger), mSeq(0) { ; }
LedgerEntrySet() : mSeq(0) { ; }
// set functions
LedgerEntrySet duplicate() const; // Make a duplicate of this set
void setTo(const LedgerEntrySet&); // Set this set to have the same contents as another
void swapWith(LedgerEntrySet&); // Swap the contents of two sets
int getSeq() const { return mSeq; }
void bumpSeq() { ++mSeq; }
void init(Ledger::ref ledger, const uint256& transactionID, uint32 ledgerID);
void clear();
Ledger::pointer& getLedger() { return mLedger; }
Ledger::ref getLedgerRef() const { return mLedger; }
// basic entry functions
SLE::pointer getEntry(const uint256& index, LedgerEntryAction&);
LedgerEntryAction hasEntry(const uint256& index) const;
void entryCache(SLE::ref); // Add this entry to the cache
void entryCreate(SLE::ref); // This entry will be created
void entryDelete(SLE::ref); // This entry will be deleted
void entryModify(SLE::ref); // This entry will be modified
// higher-level ledger functions
SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex);
SLE::pointer entryCache(LedgerEntryType letType, const uint256& uIndex);
// Directory functions.
TER dirAdd(
uint64& uNodeDir, // Node of entry.
const uint256& uRootIndex,
const uint256& uLedgerIndex);
TER dirDelete(
const bool bKeepRoot,
const uint64& uNodeDir, // Node item is mentioned in.
const uint256& uRootIndex,
const uint256& uLedgerIndex, // Item being deleted
const bool bStable);
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
// Offer functions.
TER offerDelete(const uint256& uOfferIndex);
TER offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID);
// Balance functions.
uint32 rippleTransferRate(const uint160& uIssuerID);
uint32 rippleTransferRate(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID);
STAmount rippleOwed(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID);
STAmount rippleLimit(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID);
uint32 rippleQualityIn(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID,
SField::ref sfLow = sfLowQualityIn, SField::ref sfHigh = sfHighQualityIn);
uint32 rippleQualityOut(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)
{ return rippleQualityIn(uToAccountID, uFromAccountID, uCurrencyID, sfLowQualityOut, sfHighQualityOut); }
STAmount rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
STAmount rippleTransferFee(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount);
void rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer=true);
STAmount rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);
void accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount);
STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault);
Json::Value getJson(int) const;
void calcRawMeta(Serializer&, TER result);
// iterator functions
typedef std::map<uint256, LedgerEntrySetEntry>::iterator iterator;
typedef std::map<uint256, LedgerEntrySetEntry>::const_iterator const_iterator;
bool isEmpty() const { return mEntries.empty(); }
std::map<uint256, LedgerEntrySetEntry>::const_iterator begin() const { return mEntries.begin(); }
std::map<uint256, LedgerEntrySetEntry>::const_iterator end() const { return mEntries.end(); }
std::map<uint256, LedgerEntrySetEntry>::iterator begin() { return mEntries.begin(); }
std::map<uint256, LedgerEntrySetEntry>::iterator end() { return mEntries.end(); }
static bool intersect(const LedgerEntrySet& lesLeft, const LedgerEntrySet& lesRight);
};
inline LedgerEntrySet::iterator range_begin(LedgerEntrySet& x) { return x.begin(); }
inline LedgerEntrySet::iterator range_end(LedgerEntrySet &x) { return x.end(); }
namespace boost
{
template<> struct range_mutable_iterator<LedgerEntrySet> { typedef LedgerEntrySet::iterator type; };
template<> struct range_const_iterator<LedgerEntrySet> { typedef LedgerEntrySet::const_iterator type; };
}
#endif
// vim:ts=4

View File

@@ -0,0 +1,121 @@
#include "LedgerFormats.h"
std::map<int, LedgerEntryFormat*> LedgerEntryFormat::byType;
std::map<std::string, LedgerEntryFormat*> LedgerEntryFormat::byName;
#define LEF_BASE \
<< SOElement(sfLedgerIndex, SOE_OPTIONAL) \
<< SOElement(sfLedgerEntryType, SOE_REQUIRED) \
<< SOElement(sfFlags, SOE_REQUIRED)
#define DECLARE_LEF(name, type) lef = new LedgerEntryFormat(#name, type); (*lef) LEF_BASE
static bool LEFInit()
{
LedgerEntryFormat* lef;
DECLARE_LEF(AccountRoot, ltACCOUNT_ROOT)
<< SOElement(sfAccount, SOE_REQUIRED)
<< SOElement(sfSequence, SOE_REQUIRED)
<< SOElement(sfBalance, SOE_REQUIRED)
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
<< SOElement(sfAuthorizedKey, SOE_OPTIONAL)
<< SOElement(sfEmailHash, SOE_OPTIONAL)
<< SOElement(sfWalletLocator, SOE_OPTIONAL)
<< SOElement(sfWalletSize, SOE_OPTIONAL)
<< SOElement(sfMessageKey, SOE_OPTIONAL)
<< SOElement(sfTransferRate, SOE_OPTIONAL)
<< SOElement(sfDomain, SOE_OPTIONAL)
;
DECLARE_LEF(Contract, ltCONTRACT)
<< SOElement(sfAccount, SOE_REQUIRED)
<< SOElement(sfBalance, SOE_REQUIRED)
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
<< SOElement(sfIssuer, SOE_REQUIRED)
<< SOElement(sfOwner, SOE_REQUIRED)
<< SOElement(sfExpiration, SOE_REQUIRED)
<< SOElement(sfBondAmount, SOE_REQUIRED)
<< SOElement(sfCreateCode, SOE_REQUIRED)
<< SOElement(sfFundCode, SOE_REQUIRED)
<< SOElement(sfRemoveCode, SOE_REQUIRED)
<< SOElement(sfExpireCode, SOE_REQUIRED)
;
DECLARE_LEF(DirectoryNode, ltDIR_NODE)
<< SOElement(sfIndexes, SOE_REQUIRED)
<< SOElement(sfIndexNext, SOE_OPTIONAL)
<< SOElement(sfIndexPrevious, SOE_OPTIONAL)
;
DECLARE_LEF(GeneratorMap, ltGENERATOR_MAP)
<< SOElement(sfGenerator, SOE_REQUIRED)
;
DECLARE_LEF(Nickname, ltNICKNAME)
<< SOElement(sfAccount, SOE_REQUIRED)
<< SOElement(sfMinimumOffer, SOE_OPTIONAL)
;
DECLARE_LEF(Offer, ltOFFER)
<< SOElement(sfAccount, SOE_REQUIRED)
<< SOElement(sfSequence, SOE_REQUIRED)
<< SOElement(sfTakerPays, SOE_REQUIRED)
<< SOElement(sfTakerGets, SOE_REQUIRED)
<< SOElement(sfBookDirectory, SOE_REQUIRED)
<< SOElement(sfBookNode, SOE_REQUIRED)
<< SOElement(sfOwnerNode, SOE_REQUIRED)
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
<< SOElement(sfExpiration, SOE_OPTIONAL)
;
DECLARE_LEF(RippleState, ltRIPPLE_STATE)
<< SOElement(sfBalance, SOE_REQUIRED)
<< SOElement(sfLowLimit, SOE_REQUIRED)
<< SOElement(sfHighLimit, SOE_REQUIRED)
<< SOElement(sfPreviousTxnID, SOE_REQUIRED)
<< SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED)
<< SOElement(sfLowQualityIn, SOE_OPTIONAL)
<< SOElement(sfLowQualityOut, SOE_OPTIONAL)
<< SOElement(sfHighQualityIn, SOE_OPTIONAL)
<< SOElement(sfHighQualityOut, SOE_OPTIONAL)
;
DECLARE_LEF(LedgerHashes, ltLEDGER_HASHES)
<< SOElement(sfHashes, SOE_REQUIRED)
;
return true;
}
bool LEFInitComplete = LEFInit();
LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(LedgerEntryType t)
{
std::map<int, LedgerEntryFormat*>::iterator it = byType.find(static_cast<int>(t));
if (it == byType.end())
return NULL;
return it->second;
}
LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(int t)
{
std::map<int, LedgerEntryFormat*>::iterator it = byType.find((t));
if (it == byType.end())
return NULL;
return it->second;
}
LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(const std::string& t)
{
std::map<std::string, LedgerEntryFormat*>::iterator it = byName.find((t));
if (it == byName.end())
return NULL;
return it->second;
}
// vim:ts=4

View File

@@ -0,0 +1,71 @@
#ifndef __LEDGERFORMATS__
#define __LEDGERFORMATS__
#include "SerializedObject.h"
// Used as the type of a transaction or the type of a ledger entry.
enum LedgerEntryType
{
ltINVALID = -1,
ltACCOUNT_ROOT = 'a',
ltDIR_NODE = 'd',
ltGENERATOR_MAP = 'g',
ltRIPPLE_STATE = 'r',
ltNICKNAME = 'n',
ltOFFER = 'o',
ltCONTRACT = 'c',
ltLEDGER_HASHES = 'h',
};
// Used as a prefix for computing ledger indexes (keys).
enum LedgerNameSpace
{
spaceAccount = 'a',
spaceDirNode = 'd',
spaceGenerator = 'g',
spaceNickname = 'n',
spaceRipple = 'r',
spaceOffer = 'o', // Entry for an offer.
spaceOwnerDir = 'O', // Directory of things owned by an account.
spaceBookDir = 'B', // Directory of order books.
spaceContract = 'c',
spaceHashes = 'h',
};
enum LedgerSpecificFlags
{
// ltACCOUNT_ROOT
lsfPasswordSpent = 0x00010000, // True if password set fee is spent.
// ltOFFER
lsfPassive = 0x00010000,
};
class LedgerEntryFormat
{
public:
std::string t_name;
LedgerEntryType t_type;
std::vector<SOElement::ptr> elements;
static std::map<int, LedgerEntryFormat*> byType;
static std::map<std::string, LedgerEntryFormat*> byName;
LedgerEntryFormat(const char *name, LedgerEntryType type) : t_name(name), t_type(type)
{
byName[name] = this;
byType[type] = this;
}
LedgerEntryFormat& operator<<(const SOElement& el)
{
elements.push_back(new SOElement(el));
return *this;
}
static LedgerEntryFormat* getLgrFormat(LedgerEntryType t);
static LedgerEntryFormat* getLgrFormat(const std::string& t);
static LedgerEntryFormat* getLgrFormat(int t);
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,102 @@
#include "LedgerHistory.h"
#include <string>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include "Config.h"
#include "Application.h"
#ifndef CACHED_LEDGER_NUM
#define CACHED_LEDGER_NUM 128
#endif
#ifndef CACHED_LEDGER_AGE
#define CACHED_LEDGER_AGE 900
#endif
// FIXME: Need to clean up ledgers by index at some point
LedgerHistory::LedgerHistory() : mLedgersByHash("LedgerCache", CACHED_LEDGER_NUM, CACHED_LEDGER_AGE)
{ ; }
void LedgerHistory::addLedger(Ledger::pointer ledger)
{
mLedgersByHash.canonicalize(ledger->getHash(), ledger, true);
}
void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus)
{
assert(ledger && ledger->isAccepted());
uint256 h(ledger->getHash());
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
mLedgersByHash.canonicalize(h, ledger, true);
assert(ledger);
assert(ledger->isAccepted());
assert(ledger->isImmutable());
mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash();
ledger->pendSave(fromConsensus);
}
Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index)
{
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
std::map<uint32, uint256>::iterator it(mLedgersByIndex.find(index));
if (it != mLedgersByIndex.end())
{
uint256 hash = it->second;
sl.unlock();
return getLedgerByHash(hash);
}
sl.unlock();
Ledger::pointer ret(Ledger::loadByIndex(index));
if (!ret)
return ret;
assert(ret->getLedgerSeq() == index);
sl.lock();
mLedgersByHash.canonicalize(ret->getHash(), ret);
mLedgersByIndex[ret->getLedgerSeq()] = ret->getHash();
return (ret->getLedgerSeq() == index) ? ret : Ledger::pointer();
}
Ledger::pointer LedgerHistory::getLedgerByHash(const uint256& hash)
{
Ledger::pointer ret = mLedgersByHash.fetch(hash);
if (ret)
return ret;
ret = Ledger::loadByHash(hash);
if (!ret)
return ret;
assert(ret->getHash() == hash);
return ret;
}
Ledger::pointer LedgerHistory::canonicalizeLedger(Ledger::pointer ledger, bool save)
{
assert(ledger->isImmutable());
uint256 h(ledger->getHash());
if (!save)
{ // return input ledger if not in map, otherwise, return corresponding map ledger
Ledger::pointer ret = mLedgersByHash.fetch(h);
if (ret)
return ret;
return ledger;
}
// save input ledger in map if not in map, otherwise return corresponding map ledger
boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex());
mLedgersByHash.canonicalize(h, ledger);
if (ledger->isAccepted())
mLedgersByIndex[ledger->getLedgerSeq()] = ledger->getHash();
return ledger;
}
// vim:ts=4

View File

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

View File

@@ -0,0 +1,208 @@
#include "LedgerMaster.h"
#include <boost/foreach.hpp>
#include "Application.h"
#include "RippleAddress.h"
#include "Log.h"
SETUP_LOG();
uint32 LedgerMaster::getCurrentLedgerIndex()
{
return mCurrentLedger->getLedgerSeq();
}
void LedgerMaster::addHeldTransaction(const Transaction::pointer& transaction)
{ // returns true if transaction was added
boost::recursive_mutex::scoped_lock ml(mLock);
mHeldTransactions.push_back(transaction->getSTransaction());
}
void LedgerMaster::pushLedger(Ledger::ref newLedger)
{
// Caller should already have properly assembled this ledger into "ready-to-close" form --
// all candidate transactions must already be applied
cLog(lsINFO) << "PushLedger: " << newLedger->getHash();
boost::recursive_mutex::scoped_lock ml(mLock);
if (!!mFinalizedLedger)
{
mFinalizedLedger->setClosed();
cLog(lsTRACE) << "Finalizes: " << mFinalizedLedger->getHash();
}
mFinalizedLedger = mCurrentLedger;
mCurrentLedger = newLedger;
mEngine.setLedger(newLedger);
}
void LedgerMaster::pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus)
{
assert(newLCL->isClosed() && newLCL->isAccepted());
assert(!newOL->isClosed() && !newOL->isAccepted());
if (newLCL->isAccepted())
{
assert(newLCL->isClosed());
assert(newLCL->isImmutable());
mLedgerHistory.addAcceptedLedger(newLCL, fromConsensus);
cLog(lsINFO) << "StashAccepted: " << newLCL->getHash();
}
boost::recursive_mutex::scoped_lock ml(mLock);
mFinalizedLedger = newLCL;
mCurrentLedger = newOL;
mEngine.setLedger(newOL);
}
void LedgerMaster::switchLedgers(Ledger::ref lastClosed, Ledger::ref current)
{
assert(lastClosed && current);
{
boost::recursive_mutex::scoped_lock ml(mLock);
mFinalizedLedger = lastClosed;
mFinalizedLedger->setClosed();
mFinalizedLedger->setAccepted();
mCurrentLedger = current;
}
assert(!mCurrentLedger->isClosed());
mEngine.setLedger(mCurrentLedger);
}
void LedgerMaster::storeLedger(Ledger::ref ledger)
{
mLedgerHistory.addLedger(ledger);
if (ledger->isAccepted())
mLedgerHistory.addAcceptedLedger(ledger, false);
}
Ledger::pointer LedgerMaster::closeLedger(bool recover)
{
boost::recursive_mutex::scoped_lock sl(mLock);
Ledger::pointer closingLedger = mCurrentLedger;
if (recover)
{
int recovers = 0;
for (CanonicalTXSet::iterator it = mHeldTransactions.begin(), end = mHeldTransactions.end(); it != end; ++it)
{
try
{
TER result = mEngine.applyTransaction(*it->second, tapOPEN_LEDGER);
if (isTepSuccess(result))
++recovers;
}
catch (...)
{
cLog(lsWARNING) << "Held transaction throws";
}
}
tLog(recovers != 0, lsINFO) << "Recovered " << recovers << " held transactions";
mHeldTransactions.reset(closingLedger->getHash());
}
mCurrentLedger = boost::make_shared<Ledger>(boost::ref(*closingLedger), true);
mEngine.setLedger(mCurrentLedger);
return closingLedger;
}
TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEngineParams params)
{
TER result = mEngine.applyTransaction(txn, params);
theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result);
return result;
}
void LedgerMaster::acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq)
{
mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash);
if (mMissingLedger->isComplete())
{
Ledger::pointer lgr = mMissingLedger->getLedger();
if (lgr && (lgr->getLedgerSeq() == ledgerSeq))
missingAcquireComplete(mMissingLedger);
mMissingLedger.reset();
return;
}
mMissingSeq = ledgerSeq;
if (mMissingLedger->setAccept())
mMissingLedger->addOnComplete(boost::bind(&LedgerMaster::missingAcquireComplete, this, _1));
}
void LedgerMaster::missingAcquireComplete(LedgerAcquire::pointer acq)
{
boost::recursive_mutex::scoped_lock ml(mLock);
if (acq->isFailed() && (mMissingSeq != 0))
{
cLog(lsWARNING) << "Acquire failed for " << mMissingSeq;
}
mMissingLedger.reset();
mMissingSeq = 0;
if (!acq->isFailed())
{
setFullLedger(acq->getLedger());
acq->getLedger()->pendSave(false);
}
}
void LedgerMaster::setFullLedger(Ledger::ref ledger)
{
boost::recursive_mutex::scoped_lock ml(mLock);
mCompleteLedgers.setValue(ledger->getLedgerSeq());
if ((ledger->getLedgerSeq() != 0) && mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1))
{ // we think we have the previous ledger, double check
Ledger::pointer prevLedger = getLedgerBySeq(ledger->getLedgerSeq() - 1);
if (prevLedger && (prevLedger->getHash() != ledger->getParentHash()))
{
cLog(lsWARNING) << "Ledger " << ledger->getLedgerSeq() << " invalidates prior ledger";
mCompleteLedgers.clearValue(prevLedger->getLedgerSeq());
}
}
if (mMissingLedger && mMissingLedger->isComplete())
mMissingLedger.reset();
if (mMissingLedger || !theConfig.FULL_HISTORY)
return;
if (Ledger::getPendingSaves() > 3)
{
cLog(lsINFO) << "Too many pending ledger saves";
return;
}
// see if there's a ledger gap we need to fill
if (!mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1))
{
cLog(lsINFO) << "We need the ledger before the ledger we just accepted";
acquireMissingLedger(ledger->getParentHash(), ledger->getLedgerSeq() - 1);
}
else
{
uint32 prevMissing = mCompleteLedgers.prevMissing(ledger->getLedgerSeq());
if (prevMissing != RangeSet::RangeSetAbsent)
{
cLog(lsINFO) << "Ledger " << prevMissing << " is missing";
assert(!mCompleteLedgers.hasValue(prevMissing));
Ledger::pointer nextLedger = getLedgerBySeq(prevMissing + 1);
if (nextLedger)
acquireMissingLedger(nextLedger->getParentHash(), nextLedger->getLedgerSeq() - 1);
else
{
mCompleteLedgers.clearValue(prevMissing);
cLog(lsWARNING) << "We have a gap we can't fix: " << prevMissing + 1;
}
}
}
}
// vim:ts=4

View File

@@ -0,0 +1,100 @@
#ifndef __LEDGERMASTER__
#define __LEDGERMASTER__
#include "Ledger.h"
#include "LedgerHistory.h"
#include "Peer.h"
#include "types.h"
#include "LedgerAcquire.h"
#include "Transaction.h"
#include "TransactionEngine.h"
#include "RangeSet.h"
#include "CanonicalTXSet.h"
// Tracks the current ledger and any ledgers in the process of closing
// Tracks ledger history
// Tracks held transactions
class LedgerMaster
{
boost::recursive_mutex mLock;
TransactionEngine mEngine;
Ledger::pointer mCurrentLedger; // The ledger we are currently processiong
Ledger::pointer mFinalizedLedger; // The ledger that most recently closed
LedgerHistory mLedgerHistory;
CanonicalTXSet mHeldTransactions;
RangeSet mCompleteLedgers;
LedgerAcquire::pointer mMissingLedger;
uint32 mMissingSeq;
void applyFutureTransactions(uint32 ledgerIndex);
bool isValidTransaction(const Transaction::pointer& trans);
bool isTransactionOnFutureList(const Transaction::pointer& trans);
void acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq);
void missingAcquireComplete(LedgerAcquire::pointer);
public:
LedgerMaster() : mHeldTransactions(uint256()), mMissingSeq(0) { ; }
uint32 getCurrentLedgerIndex();
ScopedLock getLock() { return ScopedLock(mLock); }
// The current ledger is the ledger we believe new transactions should go in
Ledger::pointer getCurrentLedger() { return mCurrentLedger; }
// The finalized ledger is the last closed/accepted ledger
Ledger::pointer getClosedLedger() { return mFinalizedLedger; }
TER doTransaction(const SerializedTransaction& txn, TransactionEngineParams params);
void pushLedger(Ledger::ref newLedger);
void pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus);
void storeLedger(Ledger::ref);
void setFullLedger(Ledger::ref ledger);
void switchLedgers(Ledger::ref lastClosed, Ledger::ref newCurrent);
std::string getCompleteLedgers() { return mCompleteLedgers.toString(); }
Ledger::pointer closeLedger(bool recoverHeldTransactions);
Ledger::pointer getLedgerBySeq(uint32 index)
{
if (mCurrentLedger && (mCurrentLedger->getLedgerSeq() == index))
return mCurrentLedger;
if (mFinalizedLedger && (mFinalizedLedger->getLedgerSeq() == index))
return mFinalizedLedger;
return mLedgerHistory.getLedgerBySeq(index);
}
Ledger::pointer getLedgerByHash(const uint256& hash)
{
if (hash.isZero())
return mCurrentLedger;
if (mCurrentLedger && (mCurrentLedger->getHash() == hash))
return mCurrentLedger;
if (mFinalizedLedger && (mFinalizedLedger->getHash() == hash))
return mFinalizedLedger;
return mLedgerHistory.getLedgerByHash(hash);
}
void setLedgerRangePresent(uint32 minV, uint32 maxV) { mCompleteLedgers.setRange(minV, maxV); }
void addHeldTransaction(const Transaction::pointer& trans);
void sweep(void) { mLedgerHistory.sweep(); }
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,108 @@
#include "LedgerProposal.h"
#include <boost/make_shared.hpp>
#include "key.h"
#include "Application.h"
#include "HashPrefixes.h"
DECLARE_INSTANCE(LedgerProposal);
LedgerProposal::LedgerProposal(const uint256& pLgr, uint32 seq, const uint256& tx, uint32 closeTime,
const RippleAddress& naPeerPublic, const uint256& suppression) :
mPreviousLedger(pLgr), mCurrentHash(tx), mSuppression(suppression), mCloseTime(closeTime),
mProposeSeq(seq), mPublicKey(naPeerPublic)
{
// XXX Validate key.
// if (!mKey->SetPubKey(pubKey))
// throw std::runtime_error("Invalid public key in proposal");
mPeerID = mPublicKey.getNodeID();
mTime = boost::posix_time::second_clock::universal_time();
}
LedgerProposal::LedgerProposal(const RippleAddress& naPub, const RippleAddress& naPriv,
const uint256& prevLgr, const uint256& position, uint32 closeTime) :
mPreviousLedger(prevLgr), mCurrentHash(position), mCloseTime(closeTime), mProposeSeq(0),
mPublicKey(naPub), mPrivateKey(naPriv)
{
mPeerID = mPublicKey.getNodeID();
mTime = boost::posix_time::second_clock::universal_time();
}
LedgerProposal::LedgerProposal(const uint256& prevLgr, const uint256& position, uint32 closeTime) :
mPreviousLedger(prevLgr), mCurrentHash(position), mCloseTime(closeTime), mProposeSeq(0)
{
mTime = boost::posix_time::second_clock::universal_time();
}
uint256 LedgerProposal::getSigningHash() const
{
Serializer s((32 + 32 + 32 + 256 + 256) / 8);
s.add32(sHP_Proposal);
s.add32(mProposeSeq);
s.add32(mCloseTime);
s.add256(mPreviousLedger);
s.add256(mCurrentHash);
return s.getSHA512Half();
}
bool LedgerProposal::checkSign(const std::string& signature, const uint256& signingHash)
{
return mPublicKey.verifyNodePublic(signingHash, signature);
}
bool LedgerProposal::changePosition(const uint256& newPosition, uint32 closeTime)
{
if (mProposeSeq == seqLeave)
return false;
mCurrentHash = newPosition;
mCloseTime = closeTime;
mTime = boost::posix_time::second_clock::universal_time();
++mProposeSeq;
return true;
}
void LedgerProposal::bowOut()
{
mTime = boost::posix_time::second_clock::universal_time();
mProposeSeq = seqLeave;
}
std::vector<unsigned char> LedgerProposal::sign(void)
{
std::vector<unsigned char> ret;
mPrivateKey.signNodePrivate(getSigningHash(), ret);
// XXX If this can fail, find out sooner.
// if (!mPrivateKey.signNodePrivate(getSigningHash(), ret))
// throw std::runtime_error("unable to sign proposal");
return ret;
}
Json::Value LedgerProposal::getJson() const
{
Json::Value ret = Json::objectValue;
ret["previous_ledger"] = mPreviousLedger.GetHex();
if (mProposeSeq != seqLeave)
{
ret["transaction_hash"] = mCurrentHash.GetHex();
ret["propose_seq"] = mProposeSeq;
}
ret["close_time"] = mCloseTime;
if (mPublicKey.isValid())
ret["peer_id"] = mPublicKey.humanNodePublic();
return ret;
}
// vim:ts=4

View File

@@ -0,0 +1,78 @@
#ifndef __PROPOSELEDGER__
#define __PROPOSELEDGER__
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>
#include "../json/value.h"
#include "RippleAddress.h"
#include "Serializer.h"
#include "InstanceCounter.h"
DEFINE_INSTANCE(LedgerProposal);
class LedgerProposal : private IS_INSTANCE(LedgerProposal)
{
protected:
uint256 mPreviousLedger, mCurrentHash, mSuppression;
uint32 mCloseTime, mProposeSeq;
uint160 mPeerID;
RippleAddress mPublicKey;
RippleAddress mPrivateKey; // If ours
std::string mSignature; // set only if needed
boost::posix_time::ptime mTime;
public:
static const uint32 seqLeave = 0xffffffff; // leaving the consensus process
typedef boost::shared_ptr<LedgerProposal> pointer;
// proposal from peer
LedgerProposal(const uint256& prevLgr, uint32 proposeSeq, const uint256& propose,
uint32 closeTime, const RippleAddress& naPeerPublic, const uint256& suppress);
// our first proposal
LedgerProposal(const RippleAddress& pubKey, const RippleAddress& privKey,
const uint256& prevLedger, const uint256& position, uint32 closeTime);
// an unsigned "dummy" proposal for nodes not validating
LedgerProposal(const uint256& prevLedger, const uint256& position, uint32 closeTime);
uint256 getSigningHash() const;
bool checkSign(const std::string& signature, const uint256& signingHash);
bool checkSign(const std::string& signature) { return checkSign(signature, getSigningHash()); }
bool checkSign() { return checkSign(mSignature, getSigningHash()); }
const uint160& getPeerID() const { return mPeerID; }
const uint256& getCurrentHash() const { return mCurrentHash; }
const uint256& getPrevLedger() const { return mPreviousLedger; }
const uint256& getSuppression() const { return mSuppression; }
uint32 getProposeSeq() const { return mProposeSeq; }
uint32 getCloseTime() const { return mCloseTime; }
const RippleAddress& peekPublic() const { return mPublicKey; }
std::vector<unsigned char> getPubKey() const { return mPublicKey.getNodePublic(); }
std::vector<unsigned char> sign();
void setPrevLedger(const uint256& prevLedger) { mPreviousLedger = prevLedger; }
void setSignature(const std::string& signature) { mSignature = signature; }
bool hasSignature() { return !mSignature.empty(); }
bool isPrevLedger(const uint256& pl) { return mPreviousLedger == pl; }
bool isBowOut() { return mProposeSeq == seqLeave; }
const boost::posix_time::ptime getCreateTime() { return mTime; }
bool isStale(boost::posix_time::ptime cutoff) { return mTime <= cutoff; }
bool changePosition(const uint256& newPosition, uint32 newCloseTime);
void bowOut();
Json::Value getJson() const;
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,123 @@
#include "LedgerTiming.h"
#include <cassert>
#include <boost/format.hpp>
#include "Log.h"
SETUP_LOG();
// NOTE: First and last times must be repeated
int ContinuousLedgerTiming::LedgerTimeResolution[] = { 10, 10, 20, 30, 60, 90, 120, 120 };
// Called when a ledger is open and no close is in progress -- when a transaction is received and no close
// is in process, or when a close completes. Returns the number of seconds the ledger should be be open.
bool ContinuousLedgerTiming::shouldClose(
bool anyTransactions,
int previousProposers, // proposers in the last closing
int proposersClosed, // proposers who have currently closed this ledgers
int previousMSeconds, // seconds the previous ledger took to reach consensus
int currentMSeconds, // seconds since the previous ledger closed
int idleInterval) // network's desired idle interval
{
if ((previousMSeconds < -1000) || (previousMSeconds > 600000) ||
(currentMSeconds < -1000) || (currentMSeconds > 600000))
{
cLog(lsWARNING) <<
boost::str(boost::format("CLC::shouldClose range Trans=%s, Prop: %d/%d, Secs: %d (last:%d)")
% (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed
% currentMSeconds % previousMSeconds);
return true;
}
if (!anyTransactions)
{ // no transactions so far this interval
if (proposersClosed > (previousProposers / 4)) // did we miss a transaction?
{
cLog(lsTRACE) << "no transactions, many proposers: now (" << proposersClosed << " closed, "
<< previousProposers << " before)";
return true;
}
#if 0 // This false triggers on the genesis ledger
if (previousMSeconds > (1000 * (LEDGER_IDLE_INTERVAL + 2))) // the last ledger was very slow to close
{
cLog(lsTRACE) << "was slow to converge (p=" << (previousMSeconds) << ")";
if (previousMSeconds < 2000)
return previousMSeconds;
return previousMSeconds - 1000;
}
#endif
return currentMSeconds >= (idleInterval * 1000); // normal idle
}
return true; // this ledger should close now
}
// Returns whether we have a consensus or not. If so, we expect all honest nodes
// to already have everything they need to accept a consensus. Our vote is 'locked in'.
bool ContinuousLedgerTiming::haveConsensus(
int previousProposers, // proposers in the last closing (not including us)
int currentProposers, // proposers in this closing so far (not including us)
int currentAgree, // proposers who agree with us
int currentFinished, // proposers who have validated a ledger after this one
int previousAgreeTime, // how long it took to agree on the last ledger
int currentAgreeTime, // how long we've been trying to agree
bool forReal) // deciding whether to stop consensus process
{
cLog(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d validated=%d time=%d/%d%s") %
currentProposers % previousProposers % currentAgree % currentFinished % currentAgreeTime % previousAgreeTime %
(forReal ? "" : "X"));
if (currentAgreeTime <= LEDGER_MIN_CONSENSUS)
return false;
if (currentProposers < (previousProposers * 3 / 4))
{ // Less than 3/4 of the last ledger's proposers are present, we may need more time
if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS))
{
tLog(forReal, lsTRACE) << "too fast, not enough proposers";
return false;
}
}
// If 80% of current proposers (plus us) agree on a set, we have consensus
if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80)
{
tLog(forReal, lsINFO) << "normal consensus";
return true;
}
// If 50% of the nodes on your UNL have moved on, you should declare consensus
if (((currentFinished * 100) / (currentProposers + 1)) > 50)
{
tLog(forReal, lsWARNING) << "We see no consensus, but 50% of nodes have moved on";
return true;
}
// no consensus yet
tLog(forReal, lsTRACE) << "no consensus";
return false;
}
int ContinuousLedgerTiming::getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq)
{
assert(ledgerSeq);
if ((!previousAgree) && ((ledgerSeq % LEDGER_RES_DECREASE) == 0))
{ // reduce resolution
int i = 1;
while (LedgerTimeResolution[i] != previousResolution)
++i;
return LedgerTimeResolution[i + 1];
}
if ((previousAgree) && ((ledgerSeq % LEDGER_RES_INCREASE) == 0))
{ // increase resolution
int i = 1;
while (LedgerTimeResolution[i] != previousResolution)
++i;
return LedgerTimeResolution[i - 1];
}
return previousResolution;
}

View File

@@ -0,0 +1,74 @@
#ifndef __LEDGERTIMING__
#define __LEDGERTIMING__
// The number of seconds a ledger may remain idle before closing
# define LEDGER_IDLE_INTERVAL 15
// The number of seconds a validation remains current after its ledger's close time
// This is a safety to protect against very old validations and the time it takes to adjust
// the close time accuracy window
# define LEDGER_VAL_INTERVAL 600
// The number of seconds before a close time that we consider a validation acceptable
// This protects against extreme clock errors
# define LEDGER_EARLY_INTERVAL 240
// The number of milliseconds we wait minimum to ensure participation
# define LEDGER_MIN_CONSENSUS 2000
// Initial resolution of ledger close time
# define LEDGER_TIME_ACCURACY 30
// How often to increase resolution
# define LEDGER_RES_INCREASE 8
// How often to decrease resolution
# define LEDGER_RES_DECREASE 1
// How often we check state or change positions (in milliseconds)
# define LEDGER_GRANULARITY 1000
// The percentage of active trusted validators that must be able to
// keep up with the network or we consider the network overloaded
# define LEDGER_NET_RATIO 70
// How long we consider a proposal fresh
# define PROPOSE_FRESHNESS 20
// How often we force generating a new proposal to keep ours fresh
# define PROPOSE_INTERVAL 12
// Avalanche tuning
#define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes
#define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance
#define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing
#define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance
#define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing
class ContinuousLedgerTiming
{
public:
static int LedgerTimeResolution[];
// Returns the number of seconds the ledger was or should be open
// Call when a consensus is reached and when any transaction is relayed to be added
static bool shouldClose(
bool anyTransactions,
int previousProposers, int proposersClosed,
int previousSeconds, int currentSeconds,
int idleInterval);
static bool haveConsensus(
int previousProposers, int currentProposers,
int currentAgree, int currentClosed,
int previousAgreeTime, int currentAgreeTime,
bool forReal);
static int getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq);
};
#endif

188
src/cpp/ripple/Log.cpp Normal file
View File

@@ -0,0 +1,188 @@
#include "Log.h"
#include <fstream>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/algorithm/string.hpp>
boost::recursive_mutex Log::sLock;
LogSeverity Log::sMinSeverity = lsINFO;
std::ofstream* Log::outStream = NULL;
boost::filesystem::path *Log::pathToLog = NULL;
uint32 Log::logRotateCounter = 0;
LogPartition* LogPartition::headLog = NULL;
LogPartition::LogPartition(const char *name) : mNextLog(headLog), mMinSeverity(lsWARNING)
{
const char *ptr = strrchr(name, '/');
mName = (ptr == NULL) ? name : (ptr + 1);
size_t p = mName.find(".cpp");
if (p != std::string::npos)
mName.erase(mName.begin() + p, mName.end());
headLog = this;
}
std::vector< std::pair<std::string, std::string> > LogPartition::getSeverities()
{
std::vector< std::pair<std::string, std::string> > sevs;
for (LogPartition *l = headLog; l != NULL; l = l->mNextLog)
sevs.push_back(std::make_pair(l->mName, Log::severityToString(l->mMinSeverity)));
return sevs;
}
Log::~Log()
{
std::string logMsg = boost::posix_time::to_simple_string(boost::posix_time::second_clock::universal_time());
if (!mPartitionName.empty())
logMsg += " " + mPartitionName + ":";
else
logMsg += " ";
switch (mSeverity)
{
case lsTRACE: logMsg += "TRC "; break;
case lsDEBUG: logMsg += "DBG "; break;
case lsINFO: logMsg += "NFO "; break;
case lsWARNING: logMsg += "WRN "; break;
case lsERROR: logMsg += "ERR "; break;
case lsFATAL: logMsg += "FTL "; break;
case lsINVALID: assert(false); return;
}
logMsg += oss.str();
boost::recursive_mutex::scoped_lock sl(sLock);
if (mSeverity >= sMinSeverity)
std::cerr << logMsg << std::endl;
if (outStream != NULL)
(*outStream) << logMsg << std::endl;
}
std::string Log::rotateLog(void)
{
boost::recursive_mutex::scoped_lock sl(sLock);
boost::filesystem::path abs_path;
std::string abs_path_str;
uint32 failsafe = 0;
std::string abs_new_path_str;
do {
std::string s;
std::stringstream out;
failsafe++;
if (failsafe == std::numeric_limits<uint32>::max()) {
return "unable to create new log file; too many log files!";
}
abs_path = boost::filesystem::absolute("");
abs_path /= *pathToLog;
abs_path_str = abs_path.parent_path().string();
out << logRotateCounter;
s = out.str();
abs_new_path_str = abs_path_str + "/" + s + + "_" + pathToLog->filename().string();
logRotateCounter++;
} while (boost::filesystem::exists(boost::filesystem::path(abs_new_path_str)));
outStream->close();
boost::filesystem::rename(abs_path, boost::filesystem::path(abs_new_path_str));
setLogFile(*pathToLog);
return abs_new_path_str;
}
void Log::setMinSeverity(LogSeverity s, bool all)
{
boost::recursive_mutex::scoped_lock sl(sLock);
sMinSeverity = s;
if (all)
LogPartition::setSeverity(s);
}
LogSeverity Log::getMinSeverity()
{
boost::recursive_mutex::scoped_lock sl(sLock);
return sMinSeverity;
}
std::string Log::severityToString(LogSeverity s)
{
switch (s)
{
case lsTRACE: return "Trace";
case lsDEBUG: return "Debug";
case lsINFO: return "Info";
case lsWARNING: return "Warning";
case lsERROR: return "Error";
case lsFATAL: return "Fatal";
default: assert(false); return "Unknown";
}
}
LogSeverity Log::stringToSeverity(const std::string& s)
{
if (boost::iequals(s, "trace"))
return lsTRACE;
if (boost::iequals(s, "debug"))
return lsDEBUG;
if (boost::iequals(s, "info") || boost::iequals(s, "information"))
return lsINFO;
if (boost::iequals(s, "warn") || boost::iequals(s, "warning") || boost::iequals(s, "warnings"))
return lsWARNING;
if (boost::iequals(s, "error") || boost::iequals(s, "errors"))
return lsERROR;
if (boost::iequals(s, "fatal") || boost::iequals(s, "fatals"))
return lsFATAL;
return lsINVALID;
}
void Log::setLogFile(boost::filesystem::path path)
{
std::ofstream* newStream = new std::ofstream(path.c_str(), std::fstream::app);
if (!newStream->good())
{
delete newStream;
newStream = NULL;
}
boost::recursive_mutex::scoped_lock sl(sLock);
if (outStream != NULL)
delete outStream;
outStream = newStream;
if (outStream)
Log(lsINFO) << "Starting up";
pathToLog = new boost::filesystem::path(path);
}
bool LogPartition::setSeverity(const std::string& partition, LogSeverity severity)
{
for (LogPartition *p = headLog; p != NULL; p = p->mNextLog)
if (boost::iequals(p->mName, partition))
{
p->mMinSeverity = severity;
return true;
}
return false;
}
void LogPartition::setSeverity(LogSeverity severity)
{
for (LogPartition *p = headLog; p != NULL; p = p->mNextLog)
p->mMinSeverity = severity;
}

106
src/cpp/ripple/Log.h Normal file
View File

@@ -0,0 +1,106 @@
#ifndef __LOG__
#define __LOG__
#include <sstream>
#include <string>
#include <limits>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/filesystem.hpp>
// Ensure that we don't get value.h without writer.h
#include "../json/json.h"
#include "types.h"
// Put at the beginning of a C++ file that needs its own log partition
#define SETUP_LOG() static LogPartition logPartition(__FILE__)
// Standard conditional log
#define cLog(x) if (!logPartition.doLog(x)) do {} while (0); else Log(x, logPartition)
// Log only if an additional condition 'c' is true. Condition is not computed if not needed
#define tLog(c,x) if (!logPartition.doLog(x) || !(c)) do {} while(0); else Log(x, logPartition)
// Check if should log
#define sLog(x) (logPartition.doLog(x))
enum LogSeverity
{
lsINVALID = -1, // used to indicate an invalid severity
lsTRACE = 0, // Very low-level progress information, details inside an operation
lsDEBUG = 1, // Function-level progress information, operations
lsINFO = 2, // Server-level progress information, major operations
lsWARNING = 3, // Conditions that warrant human attention, may indicate a problem
lsERROR = 4, // A condition that indicates a problem
lsFATAL = 5 // A severe condition that indicates a server problem
};
class LogPartition
{
protected:
static LogPartition* headLog;
LogPartition* mNextLog;
LogSeverity mMinSeverity;
std::string mName;
public:
LogPartition(const char *name);
bool doLog(LogSeverity s) { return s >= mMinSeverity; }
const std::string& getName() const { return mName; }
static bool setSeverity(const std::string& partition, LogSeverity severity);
static void setSeverity(LogSeverity severity);
static std::vector< std::pair<std::string, std::string> > getSeverities();
};
class Log
{
private:
Log(const Log&); // no implementation
Log& operator=(const Log&); // no implementation
protected:
static boost::recursive_mutex sLock;
static LogSeverity sMinSeverity;
static std::ofstream* outStream;
mutable std::ostringstream oss;
LogSeverity mSeverity;
std::string mPartitionName;
static boost::filesystem::path *pathToLog;
static uint32 logRotateCounter;
public:
Log(LogSeverity s) : mSeverity(s)
{ ; }
Log(LogSeverity s, const LogPartition& p) : mSeverity(s), mPartitionName(p.getName())
{ ; }
~Log();
template<typename T> std::ostream& operator<<(const T& t) const
{
return oss << t;
}
std::ostringstream& ref(void) const
{
return oss;
}
static std::string severityToString(LogSeverity);
static LogSeverity stringToSeverity(const std::string&);
static LogSeverity getMinSeverity();
static void setMinSeverity(LogSeverity, bool all);
static void setLogFile(boost::filesystem::path);
static std::string rotateLog(void);
};
#endif

File diff suppressed because it is too large Load Diff

242
src/cpp/ripple/NetworkOPs.h Normal file
View File

@@ -0,0 +1,242 @@
#ifndef __NETWORK_OPS__
#define __NETWORK_OPS__
#include <boost/interprocess/sync/interprocess_upgradable_mutex.hpp>
#include <boost/interprocess/sync/sharable_lock.hpp>
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include "AccountState.h"
#include "LedgerMaster.h"
#include "NicknameState.h"
#include "RippleState.h"
#include "SerializedValidation.h"
#include "LedgerAcquire.h"
#include "LedgerProposal.h"
// Operations that clients may wish to perform against the network
// Master operational handler, server sequencer, network tracker
class Peer;
class LedgerConsensus;
class InfoSub
{
public:
virtual ~InfoSub() { ; }
virtual void send(const Json::Value& jvObj) = 0;
};
class NetworkOPs
{
public:
enum Fault
{ // exceptions these functions can throw
IO_ERROR = 1,
NO_NETWORK = 2,
};
enum OperatingMode
{ // how we process transactions or account balance requests
omDISCONNECTED = 0, // not ready to process requests
omCONNECTED = 1, // convinced we are talking to the network
omTRACKING = 2, // convinced we agree with the network
omFULL = 3 // we have the ledger and can even validate
};
protected:
typedef boost::unordered_map<uint160,boost::unordered_set<InfoSub*> > subInfoMapType;
typedef boost::unordered_map<uint160,boost::unordered_set<InfoSub*> >::value_type subInfoMapValue;
typedef boost::unordered_map<uint160,boost::unordered_set<InfoSub*> >::iterator subInfoMapIterator;
typedef boost::unordered_map<uint160,std::pair<InfoSub*,uint32> > subSubmitMapType;
OperatingMode mMode;
bool mNeedNetworkLedger;
boost::posix_time::ptime mConnectTime;
boost::asio::deadline_timer mNetTimer;
boost::shared_ptr<LedgerConsensus> mConsensus;
boost::unordered_map<uint160,
std::list<LedgerProposal::pointer> > mStoredProposals;
LedgerMaster* mLedgerMaster;
LedgerAcquire::pointer mAcquiringLedger;
int mCloseTimeOffset;
// last ledger close
int mLastCloseProposers, mLastCloseConvergeTime;
uint256 mLastCloseHash;
uint32 mLastCloseTime;
uint32 mLastValidationTime;
// XXX Split into more locks.
boost::interprocess::interprocess_upgradable_mutex mMonitorLock;
subInfoMapType mSubAccount;
subInfoMapType mSubRTAccount;
subSubmitMapType mSubmitMap;
boost::unordered_set<InfoSub*> mSubLedger; // accepted ledgers
boost::unordered_set<InfoSub*> mSubServer; // when server changes connectivity state
boost::unordered_set<InfoSub*> mSubTransactions; // all accepted transactions
boost::unordered_set<InfoSub*> mSubRTTransactions; // all proposed and accepted transactions
void setMode(OperatingMode);
Json::Value transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType);
bool haveConsensusObject();
Json::Value pubBootstrapAccountInfo(Ledger::ref lpAccepted, const RippleAddress& naAccountID);
void pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult);
void pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,bool accepted);
public:
NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster);
// network information
uint32 getNetworkTimeNC();
uint32 getCloseTimeNC();
uint32 getValidationTimeNC();
void closeTimeOffset(int);
boost::posix_time::ptime getNetworkTimePT();
uint32 getLedgerID(const uint256& hash);
uint32 getCurrentLedgerID();
OperatingMode getOperatingMode() { return mMode; }
inline bool available() {
// XXX Later this can be relaxed to omCONNECTED
return mMode >= omTRACKING;
}
Ledger::pointer getClosedLedger() { return mLedgerMaster->getClosedLedger(); }
Ledger::pointer getCurrentLedger() { return mLedgerMaster->getCurrentLedger(); }
Ledger::pointer getLedgerByHash(const uint256& hash) { return mLedgerMaster->getLedgerByHash(hash); }
Ledger::pointer getLedgerBySeq(const uint32 seq) { return mLedgerMaster->getLedgerBySeq(seq); }
uint256 getClosedLedgerHash()
{ return mLedgerMaster->getClosedLedger()->getHash(); }
SLE::pointer getSLE(Ledger::pointer lpLedger, const uint256& uHash) { return lpLedger->getSLE(uHash); }
//
// Transaction operations
//
Transaction::pointer submitTransaction(const Transaction::pointer& tpTrans);
Transaction::pointer processTransaction(Transaction::pointer transaction);
Transaction::pointer findTransactionByID(const uint256& transactionID);
int findTransactionsBySource(const uint256& uLedger, std::list<Transaction::pointer>&, const RippleAddress& sourceAccount,
uint32 minSeq, uint32 maxSeq);
int findTransactionsByDestination(std::list<Transaction::pointer>&, const RippleAddress& destinationAccount,
uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions);
//
// Account functions
//
AccountState::pointer getAccountState(const uint256& uLedger, const RippleAddress& accountID);
SLE::pointer getGenerator(const uint256& uLedger, const uint160& uGeneratorID);
//
// Directory functions
//
STVector256 getDirNodeInfo(const uint256& uLedger, const uint256& uRootIndex,
uint64& uNodePrevious, uint64& uNodeNext);
//
// Nickname functions
//
NicknameState::pointer getNicknameState(const uint256& uLedger, const std::string& strNickname);
//
// Owner functions
//
Json::Value getOwnerInfo(const uint256& uLedger, const RippleAddress& naAccount);
Json::Value getOwnerInfo(Ledger::pointer lpLedger, const RippleAddress& naAccount);
// raw object operations
bool findRawLedger(const uint256& ledgerHash, std::vector<unsigned char>& rawLedger);
bool findRawTransaction(const uint256& transactionHash, std::vector<unsigned char>& rawTransaction);
bool findAccountNode(const uint256& nodeHash, std::vector<unsigned char>& rawAccountNode);
bool findTransactionNode(const uint256& nodeHash, std::vector<unsigned char>& rawTransactionNode);
// tree synchronization operations
bool getTransactionTreeNodes(uint32 ledgerSeq, const uint256& myNodeID,
const std::vector<unsigned char>& myNode, std::list< std::vector<unsigned char> >& newNodes);
bool getAccountStateNodes(uint32 ledgerSeq, const uint256& myNodeId,
const std::vector<unsigned char>& myNode, std::list< std::vector<unsigned char> >& newNodes);
// ledger proposal/close functions
void processTrustedProposal(LedgerProposal::pointer proposal, boost::shared_ptr<ripple::TMProposeSet> set,
RippleAddress nodePublic, uint256 checkLedger, bool sigGood);
bool gotTXData(const boost::shared_ptr<Peer>& peer, const uint256& hash,
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
bool recvValidation(const SerializedValidation::pointer& val);
SHAMap::pointer getTXMap(const uint256& hash);
bool hasTXSet(const boost::shared_ptr<Peer>& peer, const uint256& set, ripple::TxSetStatus status);
void mapComplete(const uint256& hash, SHAMap::ref map);
// network state machine
void checkState(const boost::system::error_code& result);
void switchLastClosedLedger(Ledger::pointer newLedger, bool duringConsensus); // Used for the "jump" case
bool checkLastClosedLedger(const std::vector<Peer::pointer>&, uint256& networkClosed);
int beginConsensus(const uint256& networkClosed, Ledger::ref closingLedger);
void endConsensus(bool correctLCL);
void setStandAlone() { setMode(omFULL); }
void setStateTimer();
void newLCL(int proposers, int convergeTime, const uint256& ledgerHash);
void needNetworkLedger() { mNeedNetworkLedger = true; }
void clearNeedNetworkLedger() { mNeedNetworkLedger = false; }
bool isNeedNetworkLedger() { return mNeedNetworkLedger; }
void consensusViewChange();
int getPreviousProposers() { return mLastCloseProposers; }
int getPreviousConvergeTime() { return mLastCloseConvergeTime; }
uint32 getLastCloseTime() { return mLastCloseTime; }
void setLastCloseTime(uint32 t) { mLastCloseTime = t; }
Json::Value getServerInfo();
uint32 acceptLedger();
boost::unordered_map<uint160,
std::list<LedgerProposal::pointer> >& peekStoredProposals() { return mStoredProposals; }
void storeProposal(const LedgerProposal::pointer& proposal, const RippleAddress& peerPublic);
uint256 getConsensusLCL();
// client information retrieval functions
std::vector< std::pair<uint32, uint256> >
getAffectedAccounts(const RippleAddress& account, uint32 minLedger, uint32 maxLedger);
std::vector<RippleAddress> getLedgerAffectedAccounts(uint32 ledgerSeq);
std::vector<SerializedTransaction> getLedgerTransactions(uint32 ledgerSeq);
//
// Monitoring: publisher side
//
void pubLedger(Ledger::ref lpAccepted);
void pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult);
//
// Monitoring: subscriber side
//
void subAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs,bool rt);
void unsubAccount(InfoSub* ispListener, const boost::unordered_set<RippleAddress>& vnaAccountIDs,bool rt);
bool subLedger(InfoSub* ispListener, Json::Value& jvResult);
bool unsubLedger(InfoSub* ispListener);
bool subServer(InfoSub* ispListener, Json::Value& jvResult);
bool unsubServer(InfoSub* ispListener);
bool subTransactions(InfoSub* ispListener);
bool unsubTransactions(InfoSub* ispListener);
bool subRTTransactions(InfoSub* ispListener);
bool unsubRTTransactions(InfoSub* ispListener);
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,27 @@
#ifndef __NETWORKSTATUS__
#define __NETWORKSTATUS__
struct NSBit
{ // a network status bit
const char *name, *description;
int number;
};
struct NetworkStatus
{
static const int nsbConnected=0; // connected to the network
static const int nsbAccepted=1; // accept this as the real network
static const int nsbFastSynching=2; // catching up, skipping transactions
static const int nsbSlowSynching=3; // catching up, txn by txn
static const int nsbSynched=4; // in synch with the network
static const int nsbIdentifiable=5; // not hiding our identity
static const int nsbLedgerSync=6; // participating in ledger sync
static const int nsbStuck=7; // unable to sync
static const int nsbShuttingDown=8; // node is shutting down
static const int nnbCount=32;
std::bitset<nnbCount> nsbValues;
std::map<int,NSBit> nsbData;
};
#endif

View File

@@ -0,0 +1,29 @@
#include "NicknameState.h"
NicknameState::NicknameState(SerializedLedgerEntry::pointer ledgerEntry) :
mLedgerEntry(ledgerEntry)
{
if (!mLedgerEntry || mLedgerEntry->getType() != ltNICKNAME) return;
}
bool NicknameState::haveMinimumOffer() const
{
return mLedgerEntry->isFieldPresent(sfMinimumOffer);
}
STAmount NicknameState::getMinimumOffer() const
{
return mLedgerEntry->isFieldPresent(sfMinimumOffer)
? mLedgerEntry->getFieldAmount(sfMinimumOffer)
: STAmount();
}
RippleAddress NicknameState::getAccountID() const
{
return mLedgerEntry->getFieldAccount(sfAccount);
}
void NicknameState::addJson(Json::Value& val)
{
val = mLedgerEntry->getJson(0);
}

View File

@@ -0,0 +1,37 @@
#ifndef _NICKNAMESTATE_
#define _NICKNAMESTATE_
//
// State of a nickname node.
// - Isolate ledger entry format.
//
#include "SerializedLedger.h"
#include <boost/shared_ptr.hpp>
class NicknameState
{
public:
typedef boost::shared_ptr<NicknameState> pointer;
private:
SerializedLedgerEntry::pointer mLedgerEntry;
public:
NicknameState(SerializedLedgerEntry::pointer ledgerEntry); // For accounts in a ledger
bool haveMinimumOffer() const;
STAmount getMinimumOffer() const;
RippleAddress getAccountID() const;
SerializedLedgerEntry::pointer getSLE() { return mLedgerEntry; }
const SerializedLedgerEntry& peekSLE() const { return *mLedgerEntry; }
SerializedLedgerEntry& peekSLE() { return *mLedgerEntry; }
std::vector<unsigned char> getRaw() const;
void addJson(Json::Value& value);
};
#endif
// vim:ts=4

View File

@@ -0,0 +1,17 @@
#include "Operation.h"
#include "Config.h"
/*
We also need to charge for each op
*/
namespace Script {
int Operation::getFee()
{
return(theConfig.FEE_CONTRACT_OPERATION);
}
}

318
src/cpp/ripple/Operation.h Normal file
View File

@@ -0,0 +1,318 @@
#include "Interpreter.h"
namespace Script {
// Contracts are non typed have variable data types
class Operation
{
public:
// returns false if there was an error
virtual bool work(Interpreter* interpreter)=0;
virtual int getFee();
virtual ~Operation() { ; }
};
// this is just an Int in the code
class IntOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data=interpreter->getIntData();
if(data->isInt32())
{
interpreter->pushStack( data );
return(true);
}
return(false);
}
};
class FloatOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data=interpreter->getFloatData();
if(data->isFloat())
{
interpreter->pushStack( data );
return(true);
}
return(false);
}
};
class Uint160Op : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data=interpreter->getUint160Data();
if(data->isUint160())
{
interpreter->pushStack( data );
return(true);
}
return(false);
}
};
class AddOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data1=interpreter->popStack();
Data::pointer data2=interpreter->popStack();
if( (data1->isInt32() || data1->isFloat()) &&
(data2->isInt32() || data2->isFloat()) )
{
if(data1->isFloat() || data2->isFloat()) interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()+data2->getFloat())));
else interpreter->pushStack(Data::pointer(new IntData(data1->getInt()+data2->getInt())));
return(true);
}else
{
return(false);
}
}
};
class SubOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data1=interpreter->popStack();
Data::pointer data2=interpreter->popStack();
if( (data1->isInt32() || data1->isFloat()) &&
(data2->isInt32() || data2->isFloat()) )
{
if(data1->isFloat() || data2->isFloat()) interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()-data2->getFloat())));
else interpreter->pushStack(Data::pointer(new IntData(data1->getInt()-data2->getInt())));
return(true);
}else
{
return(false);
}
}
};
class MulOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data1=interpreter->popStack();
Data::pointer data2=interpreter->popStack();
if( (data1->isInt32() || data1->isFloat()) &&
(data2->isInt32() || data2->isFloat()) )
{
if(data1->isFloat() || data2->isFloat()) interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()*data2->getFloat())));
else interpreter->pushStack(Data::pointer(new IntData(data1->getInt()*data2->getInt())));
return(true);
}else
{
return(false);
}
}
};
class DivOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data1=interpreter->popStack();
Data::pointer data2=interpreter->popStack();
if( (data1->isInt32() || data1->isFloat()) &&
(data2->isInt32() || data2->isFloat()) )
{
if(data1->isFloat() || data2->isFloat()) interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()/data2->getFloat())));
else interpreter->pushStack(Data::pointer(new IntData(data1->getInt()/data2->getInt())));
return(true);
}else
{
return(false);
}
}
};
class GtrOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data1=interpreter->popStack();
Data::pointer data2=interpreter->popStack();
if( (data1->isInt32() || data1->isFloat()) &&
(data2->isInt32() || data2->isFloat()) )
{
interpreter->pushStack(Data::pointer(new BoolData(data1->getFloat()>data2->getFloat())));
return(true);
}else
{
return(false);
}
}
};
class LessOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data1=interpreter->popStack();
Data::pointer data2=interpreter->popStack();
if( (data1->isInt32() || data1->isFloat()) &&
(data2->isInt32() || data2->isFloat()) )
{
interpreter->pushStack(Data::pointer(new FloatData(data1->getFloat()<data2->getFloat())));
return(true);
}else
{
return(false);
}
}
};
class ModOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data1=interpreter->popStack();
Data::pointer data2=interpreter->popStack();
if( data1->isInt32() && data2->isInt32() )
{
interpreter->pushStack(Data::pointer(new IntData(data1->getInt()%data2->getInt())));
return(true);
}else
{
return(false);
}
}
};
class StartBlockOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer offset=interpreter->getIntData();
return(interpreter->startBlock(offset->getInt()));
}
};
class EndBlockOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
return(interpreter->endBlock());
}
};
class StopOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
interpreter->stop();
return(true);
}
};
class AcceptDataOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer data=interpreter->popStack();
if(data->isInt32())
{
interpreter->pushStack( interpreter->getAcceptData(data->getInt()) );
return(true);
}
return(false);
}
};
class JumpIfOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer offset=interpreter->getIntData();
Data::pointer cond=interpreter->popStack();
if(cond->isBool() && offset->isInt32())
{
if(cond->isTrue())
{
return(interpreter->jumpTo(offset->getInt()));
}
return(true);
}
return(false);
}
};
class JumpOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer offset=interpreter->getIntData();
if(offset->isInt32())
{
return(interpreter->jumpTo(offset->getInt()));
}
return(false);
}
};
class SendXRPOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer sourceID=interpreter->popStack();
Data::pointer destID=interpreter->popStack();
Data::pointer amount=interpreter->popStack();
if(sourceID->isUint160() && destID->isUint160() && amount->isInt32() && interpreter->canSign(sourceID->getUint160()))
{
// make sure:
// source is either, this contract, issuer, or acceptor
// TODO do the send
//interpreter->pushStack( send result);
return(true);
}
return(false);
}
};
class GetDataOp : public Operation
{
public:
bool work(Interpreter* interpreter)
{
Data::pointer index=interpreter->popStack();
if(index->isInt32())
{
interpreter->pushStack( interpreter->getContractData(index->getInt()));
return(true);
}
return(false);
}
};
}

View File

@@ -0,0 +1,23 @@
#include "OrderBook.h"
#include "Ledger.h"
OrderBook::pointer OrderBook::newOrderBook(SerializedLedgerEntry::pointer ledgerEntry)
{
if(ledgerEntry->getType() != ltOFFER) return( OrderBook::pointer());
return( OrderBook::pointer(new OrderBook(ledgerEntry)));
}
OrderBook::OrderBook(SerializedLedgerEntry::pointer ledgerEntry)
{
const STAmount saTakerGets = ledgerEntry->getFieldAmount(sfTakerGets);
const STAmount saTakerPays = ledgerEntry->getFieldAmount(sfTakerPays);
mCurrencyIn = saTakerGets.getCurrency();
mCurrencyOut = saTakerPays.getCurrency();
mIssuerIn = saTakerGets.getIssuer();
mIssuerOut = saTakerPays.getIssuer();
mBookBase=Ledger::getBookBase(mCurrencyOut,mIssuerOut,mCurrencyIn,mIssuerIn);
}
// vim:ts=4

View File

@@ -0,0 +1,34 @@
#include "SerializedLedger.h"
#include <boost/shared_ptr.hpp>
/*
Encapsulates the SLE for an orderbook
*/
class OrderBook
{
uint256 mBookBase;
uint160 mCurrencyIn;
uint160 mCurrencyOut;
uint160 mIssuerIn;
uint160 mIssuerOut;
//SerializedLedgerEntry::pointer mLedgerEntry;
OrderBook(SerializedLedgerEntry::pointer ledgerEntry); // For accounts in a ledger
public:
typedef boost::shared_ptr<OrderBook> pointer;
// returns NULL if ledgerEntry doesn't point to an order
// if ledgerEntry is an Order it creates the OrderBook this order would live in
static OrderBook::pointer newOrderBook(SerializedLedgerEntry::pointer ledgerEntry);
uint256& getBookBase(){ return(mBookBase); }
uint160& getCurrencyIn(){ return(mCurrencyIn); }
uint160& getCurrencyOut(){ return(mCurrencyOut); }
uint160& getIssuerIn(){ return(mIssuerIn); }
uint160& getIssuerOut(){ return(mIssuerOut); }
// looks through the best offers to see how much it would cost to take the given amount
STAmount& getTakePrice(STAmount& takeAmount);
};

View File

@@ -0,0 +1,56 @@
#include "OrderBookDB.h"
#include "Log.h"
#include <boost/foreach.hpp>
// TODO: this would be way faster if we could just look under the order dirs
OrderBookDB::OrderBookDB(Ledger::pointer ledger)
{
// walk through the entire ledger looking for orderbook entries
uint256 currentIndex=ledger->getFirstLedgerIndex();
while(currentIndex.isNonZero())
{
SLE::pointer entry=ledger->getSLE(currentIndex);
OrderBook::pointer book=OrderBook::newOrderBook(entry);
if(book)
{
if( mKnownMap.find(book->getBookBase()) != mKnownMap.end() )
{
mKnownMap[book->getBookBase()]=true;
if(!book->getCurrencyIn())
{ // XRP
mXRPOrders.push_back(book);
}else
{
mIssuerMap[book->getIssuerIn()].push_back(book);
}
}
}
currentIndex=ledger->getNextLedgerIndex(currentIndex);
}
}
// return list of all orderbooks that want IssuerID
std::vector<OrderBook::pointer>& OrderBookDB::getBooks(const uint160& issuerID)
{
if( mIssuerMap.find(issuerID) == mIssuerMap.end() ) return mEmptyVector;
else return( mIssuerMap[issuerID]);
}
// return list of all orderbooks that want this issuerID and currencyID
void OrderBookDB::getBooks(const uint160& issuerID, const uint160& currencyID, std::vector<OrderBook::pointer>& bookRet)
{
if( mIssuerMap.find(issuerID) == mIssuerMap.end() )
{
BOOST_FOREACH(OrderBook::pointer book, mIssuerMap[issuerID])
{
if(book->getCurrencyIn()==currencyID)
{
bookRet.push_back(book);
}
}
}
}

View File

@@ -0,0 +1,30 @@
#include "Ledger.h"
#include "OrderBook.h"
/*
we can eventually make this cached and just update it as transactions come in.
But for now it is probably faster to just generate it each time
*/
class OrderBookDB
{
std::vector<OrderBook::pointer> mEmptyVector;
std::vector<OrderBook::pointer> mXRPOrders;
std::map<uint160, std::vector<OrderBook::pointer> > mIssuerMap;
std::map<uint256, bool > mKnownMap;
public:
OrderBookDB(Ledger::pointer ledger);
// return list of all orderbooks that want XRP
std::vector<OrderBook::pointer>& getXRPInBooks(){ return mXRPOrders; }
// return list of all orderbooks that want IssuerID
std::vector<OrderBook::pointer>& getBooks(const uint160& issuerID);
// return list of all orderbooks that want this issuerID and currencyID
void getBooks(const uint160& issuerID, const uint160& currencyID, std::vector<OrderBook::pointer>& bookRet);
// returns the best rate we can find
float getPrice(uint160& currencyIn,uint160& currencyOut);
};

View File

@@ -0,0 +1,53 @@
#include "PackedMessage.h"
void PackedMessage::encodeHeader(unsigned size, int type)
{
assert(mBuffer.size() >= HEADER_SIZE);
mBuffer[0] = static_cast<boost::uint8_t>((size >> 24) & 0xFF);
mBuffer[1] = static_cast<boost::uint8_t>((size >> 16) & 0xFF);
mBuffer[2] = static_cast<boost::uint8_t>((size >> 8) & 0xFF);
mBuffer[3] = static_cast<boost::uint8_t>(size & 0xFF);
mBuffer[4] = static_cast<boost::uint8_t>((type >> 8) & 0xFF);
mBuffer[5] = static_cast<boost::uint8_t>(type & 0xFF);
}
PackedMessage::PackedMessage(const ::google::protobuf::Message &message, int type)
{
unsigned msg_size = message.ByteSize();
assert(msg_size);
mBuffer.resize(HEADER_SIZE + msg_size);
encodeHeader(msg_size, type);
if (msg_size)
{
message.SerializeToArray(&mBuffer[HEADER_SIZE], msg_size);
#ifdef DEBUG
// std::cerr << "PackedMessage: type=" << type << ", datalen=" << msg_size << std::endl;
#endif
}
}
bool PackedMessage::operator == (const PackedMessage& other)
{
return (mBuffer == other.mBuffer);
}
unsigned PackedMessage::getLength(std::vector<uint8_t>& buf)
{
if(buf.size() < HEADER_SIZE)
return 0;
int ret = buf[0];
ret <<= 8; ret |= buf[1]; ret <<= 8; ret |= buf[2]; ret <<= 8; ret |= buf[3];
return ret;
}
int PackedMessage::getType(std::vector<uint8_t>& buf)
{
if(buf.size() < HEADER_SIZE)
return 0;
int ret = buf[4];
ret <<= 8; ret |= buf[5];
return ret;
}

View File

@@ -0,0 +1,76 @@
//
// packaging of messages into length/type-prepended buffers
// ready for transmission.
#ifndef PACKEDMESSAGE_H
#define PACKEDMESSAGE_H
#include <string>
#include <cassert>
#include <vector>
#include <cstdio>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/cstdint.hpp>
#include "ripple.pb.h"
// The header size for packed messages
// len(4)+type(2)
const unsigned HEADER_SIZE = 6;
// PackedMessage implements simple "packing" of protocol buffers Messages into
// a string prepended by a header specifying the message length.
// MessageType should be a Message class generated by the protobuf compiler.
//
class PackedMessage : public boost::enable_shared_from_this<PackedMessage>
{
std::vector<uint8_t> mBuffer;
// Encodes the size and type into a header at the beginning of buf
//
void encodeHeader(unsigned size, int type);
public:
typedef boost::shared_ptr< ::google::protobuf::Message > MessagePointer;
typedef boost::shared_ptr<PackedMessage> pointer;
PackedMessage(const ::google::protobuf::Message& message, int type);
std::vector<uint8_t>& getBuffer() { return(mBuffer); }
static unsigned getLength(std::vector<uint8_t>& buf);
static int getType(std::vector<uint8_t>& buf);
bool operator == (const PackedMessage& other);
/*
void setMsg(MessagePointer msg, int type);
MessagePointer getMsg();
// Pack the message into the given data_buffer. The buffer is resized to
// exactly fit the message.
// Return false in case of an error, true if successful.
//
bool pack(data_buffer& buf) const;
// Given a buffer with the first HEADER_SIZE bytes representing the header,
// decode the header and return the message length. Return 0 in case of
// an error.
//
unsigned decodeHeader(const data_buffer& buf) const;
// Unpack and store a message from the given packed buffer.
// Return true if unpacking successful, false otherwise.
//
bool unpack(const data_buffer& buf);
*/
};
#endif /* PACKEDMESSAGE_H */

View File

@@ -0,0 +1,120 @@
#include "ParseSection.h"
#include "utils.h"
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#define SECTION_DEFAULT_NAME ""
section ParseSection(const std::string& strInput, const bool bTrim)
{
std::string strData(strInput);
std::vector<std::string> vLines;
section secResult;
// Convert DOS format to unix.
boost::algorithm::replace_all(strData, "\r\n", "\n");
// Convert MacOS format to unix.
boost::algorithm::replace_all(strData, "\r", "\n");
boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of("\n"));
// Set the default section name.
std::string strSection = SECTION_DEFAULT_NAME;
// Initialize the default section.
secResult[strSection] = section::mapped_type();
// Parse each line.
BOOST_FOREACH(std::string& strValue, vLines)
{
if (strValue.empty() || strValue[0] == '#')
{
// Blank line or comment, do nothing.
nothing();
}
else if (strValue[0] == '[' && strValue[strValue.length()-1] == ']') {
// New section.
strSection = strValue.substr(1, strValue.length()-2);
secResult[strSection] = section::mapped_type();
}
else
{
// Another line for section.
if (bTrim)
boost::algorithm::trim(strValue);
if (!strValue.empty())
secResult[strSection].push_back(strValue);
}
}
return secResult;
}
void sectionEntriesPrint(std::vector<std::string>* vspEntries, const std::string& strSection)
{
std::cerr << "[" << strSection << "]" << std::endl;
if (vspEntries)
{
BOOST_FOREACH(std::string& strValue, *vspEntries)
{
std::cerr << strValue << std::endl;
}
}
}
void sectionPrint(section secInput)
{
BOOST_FOREACH(section::value_type& pairSection, secInput)
{
sectionEntriesPrint(&pairSection.second, pairSection.first);
}
}
section::mapped_type* sectionEntries(section& secSource, const std::string& strSection)
{
section::iterator it;
section::mapped_type* smtResult;
it = secSource.find(strSection);
if (it == secSource.end())
{
smtResult = 0;
}
else
{
//section::mapped_type& vecEntries = it->second;
smtResult = &(it->second);
}
return smtResult;
}
int sectionCount(section& secSource, const std::string& strSection)
{
section::mapped_type* pmtEntries = sectionEntries(secSource, strSection);
return pmtEntries ? -1 : pmtEntries->size();
}
bool sectionSingleB(section& secSource, const std::string& strSection, std::string& strValue)
{
section::mapped_type* pmtEntries = sectionEntries(secSource, strSection);
bool bSingle = pmtEntries && 1 == pmtEntries->size();
if (bSingle)
{
strValue = (*pmtEntries)[0];
}
return bSingle;
}
// vim:ts=4

View File

@@ -0,0 +1,17 @@
#ifndef _PARSE_SECTION_
#define _PARSE_SECTION_
#include <map>
#include <vector>
#include <string>
typedef std::map<const std::string, std::vector<std::string> > section;
section ParseSection(const std::string& strInput, const bool bTrim);
void sectionPrint(section secInput);
void sectionEntriesPrint(std::vector<std::string>* vspEntries, const std::string& strSection);
bool sectionSingleB(section& secSource, const std::string& strSection, std::string& strValue);
int sectionCount(section& secSource, const std::string& strSection);
section::mapped_type* sectionEntries(section& secSource, const std::string& strSection);
#endif

View File

@@ -0,0 +1,267 @@
#include "Pathfinder.h"
#include "Application.h"
#include "RippleLines.h"
#include "Log.h"
#include <boost/foreach.hpp>
/*
JED: V IIII
we just need to find a succession of the highest quality paths there until we find enough width
Don't do branching within each path
We have a list of paths we are working on but how do we compare the ones that are terminating in a different currency?
Loops
TODO: what is a good way to come up with multiple paths?
Maybe just change the sort criteria?
first a low cost one and then a fat short one?
OrderDB:
getXRPOffers();
// return list of all orderbooks that want XRP
// return list of all orderbooks that want IssuerID
// return list of all orderbooks that want this issuerID and currencyID
*/
/*
Test sending to XRP
Test XRP to XRP
Test offer in middle
Test XRP to USD
Test USD to EUR
*/
// we sort the options by:
// cost of path
// length of path
// width of path
// correct currency at the end
bool sortPathOptions(PathOption::pointer first, PathOption::pointer second)
{
if(first->mTotalCost<second->mTotalCost) return(true);
if(first->mTotalCost>second->mTotalCost) return(false);
if(first->mCorrectCurrency && !second->mCorrectCurrency) return(true);
if(!first->mCorrectCurrency && second->mCorrectCurrency) return(false);
if(first->mPath.getElementCount()<second->mPath.getElementCount()) return(true);
if(first->mPath.getElementCount()>second->mPath.getElementCount()) return(false);
if(first->mMinWidth<second->mMinWidth) return true;
return false;
}
PathOption::PathOption(uint160& srcAccount,uint160& srcCurrencyID,const uint160& dstCurrencyID)
{
mCurrentAccount=srcAccount;
mCurrencyID=srcCurrencyID;
mCorrectCurrency=(srcCurrencyID==dstCurrencyID);
mQuality=0;
mMinWidth=STAmount(dstCurrencyID,99999,80); // this will get lowered when we convert back to the correct currency
}
PathOption::PathOption(PathOption::pointer other)
{
// TODO:
}
Pathfinder::Pathfinder(RippleAddress& srcAccountID, RippleAddress& dstAccountID, uint160& srcCurrencyID, STAmount dstAmount) :
mSrcAccountID(srcAccountID.getAccountID()), mDstAccountID(dstAccountID.getAccountID()), mDstAmount(dstAmount), mSrcCurrencyID(srcCurrencyID), mOrderBook(theApp->getMasterLedger().getCurrentLedger())
{
mLedger=theApp->getMasterLedger().getCurrentLedger();
}
bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet)
{
if(mLedger) {
std::queue<STPath> pqueue;
STPathElement ele(mSrcAccountID,
mSrcCurrencyID,
uint160());
STPath path;
path.addElement(ele);
pqueue.push(path);
while(pqueue.size()) {
STPath path = pqueue.front();
pqueue.pop();
// get the first path from the queue
ele = path.mPath.back();
// get the last node from the path
if (ele.mAccountID == mDstAccountID) {
path.mPath.erase(path.mPath.begin());
path.mPath.erase(path.mPath.begin() + path.mPath.size()-1);
if (path.mPath.size() == 0) {
continue;
}
retPathSet.addPath(path);
return true;
}
// found the destination
if (!ele.mCurrencyID) {
BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXRPInBooks())
{
//if (!path.hasSeen(line->getAccountIDPeer().getAccountID()))
{
STPath new_path(path);
STPathElement new_ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
new_path.mPath.push_back(new_ele);
new_path.mCurrencyID = book->getCurrencyOut();
new_path.mCurrentAccount = book->getCurrencyOut();
pqueue.push(new_path);
}
}
} else {
RippleLines rippleLines(ele.mAccountID);
BOOST_FOREACH(RippleState::pointer line,rippleLines.getLines())
{
if (!path.hasSeen(line->getAccountIDPeer().getAccountID()))
{
STPath new_path(path);
STPathElement new_ele(line->getAccountIDPeer().getAccountID(),
ele.mCurrencyID,
uint160());
new_path.mPath.push_back(new_ele);
pqueue.push(new_path);
}
} // BOOST_FOREACHE
// every offer that wants the source currency
std::vector<OrderBook::pointer> books;
mOrderBook.getBooks(path.mCurrentAccount, path.mCurrencyID, books);
BOOST_FOREACH(OrderBook::pointer book,books)
{
STPath new_path(path);
STPathElement new_ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
new_path.mPath.push_back(new_ele);
new_path.mCurrentAccount=book->getIssuerOut();
new_path.mCurrencyID=book->getCurrencyOut();
pqueue.push(new_path);
}
} // else
// enumerate all adjacent nodes, construct a new path and push it into the queue
} // While
} // if there is a ledger
return false;
}
bool Pathfinder::checkComplete(STPathSet& retPathSet)
{
if(mCompletePaths.size())
{ // TODO: look through these and pick the most promising
int count=0;
BOOST_FOREACH(PathOption::pointer pathOption,mCompletePaths)
{
retPathSet.addPath(pathOption->mPath);
count++;
if(count>2) return(true);
}
return(true);
}
return(false);
}
// get all the options from this accountID
// if source is XRP
// every offer that wants XRP
// else
// every ripple line that starts with the source currency
// every offer that we can take that wants the source currency
void Pathfinder::addOptions(PathOption::pointer tail)
{
if(!tail->mCurrencyID)
{ // source XRP
BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXRPInBooks())
{
PathOption::pointer pathOption(new PathOption(tail));
STPathElement ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
pathOption->mPath.addElement(ele);
pathOption->mCurrentAccount=book->getIssuerOut();
pathOption->mCurrencyID=book->getCurrencyOut();
addPathOption(pathOption);
}
}else
{ // ripple
RippleLines rippleLines(tail->mCurrentAccount);
BOOST_FOREACH(RippleState::pointer line,rippleLines.getLines())
{
// TODO: make sure we can move in the correct direction
STAmount balance=line->getBalance();
if(balance.getCurrency()==tail->mCurrencyID)
{ // we have a ripple line from the tail to somewhere else
PathOption::pointer pathOption(new PathOption(tail));
STPathElement ele(line->getAccountIDPeer().getAccountID(), uint160(), uint160());
pathOption->mPath.addElement(ele);
pathOption->mCurrentAccount=line->getAccountIDPeer().getAccountID();
addPathOption(pathOption);
}
}
// every offer that wants the source currency
std::vector<OrderBook::pointer> books;
mOrderBook.getBooks(tail->mCurrentAccount, tail->mCurrencyID, books);
BOOST_FOREACH(OrderBook::pointer book,books)
{
PathOption::pointer pathOption(new PathOption(tail));
STPathElement ele(uint160(), book->getCurrencyOut(), book->getIssuerOut());
pathOption->mPath.addElement(ele);
pathOption->mCurrentAccount=book->getIssuerOut();
pathOption->mCurrencyID=book->getCurrencyOut();
addPathOption(pathOption);
}
}
}
void Pathfinder::addPathOption(PathOption::pointer pathOption)
{
if(pathOption->mCurrencyID==mDstAmount.getCurrency())
{
pathOption->mCorrectCurrency=true;
if(pathOption->mCurrentAccount==mDstAccountID)
{ // this path is complete
mCompletePaths.push_back(pathOption);
}else mBuildingPaths.push_back(pathOption);
}
else
{
pathOption->mCorrectCurrency=false;
mBuildingPaths.push_back(pathOption);
}
}
// vim:ts=4

Some files were not shown because too many files have changed in this diff Show More