Merge branch 'master' of github.com:jedmccaleb/NewCoin

This commit is contained in:
jed
2012-06-18 07:28:07 -07:00
21 changed files with 769 additions and 425 deletions

View File

@@ -40,5 +40,6 @@ public:
uint64 getBigInt(int colIndex);
void escape(const unsigned char* start,int size,std::string& retStr);
};
// vim:ts=4

View File

@@ -5,9 +5,9 @@
Database::Database(const char* host,const char* user,const char* pass) : mNumCol(0)
{
mDBPass=pass;
mHost=host;
mUser=user;
mDBPass = pass;
mHost = host;
mUser = user;
}
Database::~Database()
@@ -17,68 +17,80 @@ Database::~Database()
bool Database::getNull(const char* colName)
{
int index;
if(getColNumber(colName,&index))
if (getColNumber(colName,&index))
{
return(getNull(index));
return getNull(index);
}
return true;
}
char* Database::getStr(const char* colName,std::string& retStr)
{
int index;
if(getColNumber(colName,&index))
if (getColNumber(colName,&index))
{
return(getStr(index,retStr));
return getStr(index,retStr);
}
return(NULL);
return NULL;
}
int32 Database::getInt(const char* colName)
{
int index;
if(getColNumber(colName,&index))
if (getColNumber(colName,&index))
{
return(getInt(index));
return getInt(index);
}
return(0);
return 0;
}
float Database::getFloat(const char* colName)
{
int index;
if(getColNumber(colName,&index))
if (getColNumber(colName,&index))
{
return(getFloat(index));
return getFloat(index);
}
return(0);
return 0;
}
bool Database::getBool(const char* colName)
{
int index;
if(getColNumber(colName,&index))
if (getColNumber(colName,&index))
{
return(getBool(index));
return getBool(index);
}
return(0);
return 0;
}
int Database::getBinary(const char* colName,unsigned char* buf,int maxSize)
{
int index;
if(getColNumber(colName,&index))
if (getColNumber(colName,&index))
{
return(getBinary(index,buf,maxSize));
}
return(0);
}
std::vector<unsigned char> Database::getBinary(const char* colName)
std::vector<unsigned char> Database::getBinary(const std::string& strColName)
{
int index;
if (getColNumber(colName,&index))
if (getColNumber(strColName.c_str(), &index))
{
return getBinary(index);
}
@@ -86,37 +98,44 @@ std::vector<unsigned char> Database::getBinary(const char* colName)
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))
if (getColNumber(colName,&index))
{
return(getBigInt(index));
return getBigInt(index);
}
return(0);
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++)
for (unsigned int n=0; n<mColNameTable.size(); n++)
{
if(strcmp(colName,mColNameTable[n].c_str())==0)
if (strcmp(colName,mColNameTable[n].c_str())==0)
{
*retIndex=n;
return(true);
}
}
return(false);
return false;
}
#if 0
int Database::getSingleDBValueInt(const char* sql)
{
int ret;
if( executeSQL(sql) && startIterRows()
if ( executeSQL(sql) && startIterRows()
{
ret=getInt(0);
endIterRows();
@@ -134,7 +153,7 @@ int Database::getSingleDBValueInt(const char* sql)
float Database::getSingleDBValueFloat(const char* sql)
{
float ret;
if(executeSQL(sql) && startIterRows() && getNextRow())
if (executeSQL(sql) && startIterRows() && getNextRow())
{
ret=getFloat(0);
endIterRows();
@@ -152,7 +171,7 @@ float Database::getSingleDBValueFloat(const char* sql)
char* Database::getSingleDBValueStr(const char* sql,std::string& retStr)
{
char* ret;
if(executeSQL(sql) && startIterRows())
if (executeSQL(sql) && startIterRows())
{
ret=getStr(0,retStr);
endIterRows();

View File

@@ -4,6 +4,7 @@
#include <string>
#include <vector>
#include "../src/types.h"
#include "../src/utils.h"
#define SQL_FOREACH(_db, _strQuery) \
if ((_db)->executeSQL(_strQuery)) \
@@ -58,12 +59,15 @@ public:
// 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 char* colName);
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;

View File

@@ -1,46 +1,65 @@
#
# Sample newcoind.cfg
#
# This file is UTF-8 with Dos, UNIX, or Mac style end of lines.
# Blank lines and lines beginning with '#' are ignored.
# Undefined sections are reserved.
# No escapes are currently defined.
# This file should be named newcoind.cfg. This file is UTF-8 with Dos, UNIX,
# or Mac style end of lines. Blank lines and lines beginning with '#' are
# ignored. Undefined sections are reserved. No escapes are currently defined.
#
# When you launch newcoind, it will attempt to find this file.
#
# You may specify the location of this file with --conf=<path>. The base
# directory for other files will be the directory containing this file.
# --conf=<path>:
# You may specify the location of this file with --conf=<path>. The config
# directory is the directory containing this file. The data directory is a
# the subdirectory named "dbs".
#
# Windows:
# This file is named newcoind.cfg.
# The base directory for this configuration file and other information is the
# same directory as the newcoind program.
# Windows and no --conf:
# The config directory is the same directory as the newcoind program. The
# data directory is a the subdirectory named "dbs".
#
# Other OSes:
# This file may be named newcoind.cfg. The file will be looked for in the
# following order:
# Other OSes and no --conf:
# This file will be looked for in these places in the following order:
# ./newcoind.cfg
# $XDG_CONFIG_HOME/newcoin/newcoind.cfg
#
# If newcoind.cfg, is found in the current working directory, the directory
# will be used as the base directory for other information. Otherwise, the
# base directory for data is:
# If newcoind.cfg, is found in the current working directory, the directory
# will be used as the config directory. The data directory is a the
# subdirectory named "dbs".
#
# Otherwise, the data directory data is:
# $XDG_DATA_HOME/newcoin/
#
# Note: $XDG_CONFIG_HOME defaults to $HOME/.config
# $XDG_DATA_HOME defaults to $HOME/.local/share
#
# To perform validation, one of these sections must be provided:
# [validation_key], [validation_password], or [validation_seed].
# [unl_default]:
# Specifies how to bootstrap the UNL list. The UNL list is based on a
# validators.txt file and is maintained in the databases. When newcoind
# starts up, if the databases are missing or are obsolete due to an upgrade
# of newcoind, newcoind will reconstruct the UNL list as specified here.
#
# If this entry is not present or empty, newcoind will look for a validators.txt in the
# config directory. If not found there, it will attempt to retrieve the file
# from the newcoin foundation's web site.
#
# This entry is also used by the RPC command unl_load.
#
# Specify the file by specifying its full path.
#
# Examples: C:/home/johndoe/newcoin/validators.txt
# /home/johndoe/newcoin/validators.txt
#
# [peer_ip]:
# IP address or domain to bind to if allowing external connections from peers.
# IP address or domain to bind to allow external connections from peers.
# Defaults to not allow external connections from peers.
#
# Examples: 0.0.0.0 - Bind on all interfaces.
#
# [peer_port]:
# Port to bind to if allowing external connections from peers.
# Port to bind to allow external connections from peers.
#
# [rpc_ip]:
# IP address or domain to bind to if allowing insecure RPC connections.
# IP address or domain to bind to allow insecure RPC connections.
# Defaults to not allow RPC connections.
#
# [rpc_port]:
# Port to bind to if allowing insecure RPC connections.
@@ -48,14 +67,24 @@
# [rpc_allow_remote]:
# 0 or 1. 0 only allows RPC connections from 127.0.0.1. [default 0]
#
# [websocket_ip]:
# IP address or domain to bind to allow client connections.
#
# Examples: 0.0.0.0 - Bind on all interfaces.
# 127.0.0.1 - Bind on localhost interface. Only local programs may connect.
#
# [websocket_port]:
# Port to bind to allow client connections.
#
# [validation_seed]:
# To perform validation, this section should contain either a validation seed or key.
# The validation seed is used to generate the validation public/private key pair.
# To obtain a validation seed, use the validation_create command.
#
# Examples: RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE
# shfArahZT9Q9ckTf3s1psJ7C7qzVN
#
[peer_ip]
0.0.0.0

View File

@@ -58,11 +58,11 @@ void Application::run()
//
// Construct databases.
//
mTxnDB = new DatabaseCon("transaction.db", TxnDBInit, TxnDBCount);
mLedgerDB = new DatabaseCon("ledger.db", LedgerDBInit, LedgerDBCount);
mWalletDB = new DatabaseCon("wallet.db", WalletDBInit, WalletDBCount);
mHashNodeDB = new DatabaseCon("hashnode.db", HashNodeDBInit, HashNodeDBCount);
mNetNodeDB = new DatabaseCon("netnode.db", NetNodeDBInit, NetNodeDBCount);
mTxnDB = new DatabaseCon("transaction.db", TxnDBInit, TxnDBCount);
mLedgerDB = new DatabaseCon("ledger.db", LedgerDBInit, LedgerDBCount);
mWalletDB = new DatabaseCon("wallet.db", WalletDBInit, WalletDBCount);
mHashNodeDB = new DatabaseCon("hashnode.db", HashNodeDBInit, HashNodeDBCount);
mNetNodeDB = new DatabaseCon("netnode.db", NetNodeDBInit, NetNodeDBCount);
//
// Begin validation and ip maintenance.
@@ -70,10 +70,15 @@ void Application::run()
//
mWallet.start();
//
// Set up UNL.
//
getUNL().nodeBootstrap();
//
// Allow peer connections.
//
if(!theConfig.PEER_IP.empty() && theConfig.PEER_PORT)
if (!theConfig.PEER_IP.empty() && theConfig.PEER_PORT)
{
mPeerDoor = new PeerDoor(mIOService);
}
@@ -85,7 +90,7 @@ void Application::run()
//
// Allow RPC connections.
//
if(!theConfig.RPC_IP.empty() && theConfig.RPC_PORT)
if (!theConfig.RPC_IP.empty() && theConfig.RPC_PORT)
{
mRPCDoor = new RPCDoor(mIOService);
}
@@ -122,7 +127,6 @@ void Application::run()
mNetOps.setStateTimer(0);
// temporary
mIOService.run(); // This blocks
std::cout << "Done." << std::endl;

View File

@@ -3,32 +3,36 @@
#include "ParseSection.h"
#include "utils.h"
#include <iostream>
#include <fstream>
#include <boost/lexical_cast.hpp>
// Fees are in XNS raw.
#define DEFAULT_FEE_ACCOUNT_CREATE 1000
#define DEFAULT_FEE_NICKNAME_CREATE 1000
#define DEFAULT_FEE_DEFAULT 100
#include <fstream>
#include <iostream>
#define CONFIG_FILE_NAME SYSTEM_NAME "d.cfg" // newcoind.cfg
#define SECTION_ACCOUNT_PROBE_MAX "account_probe_max"
#define SECTION_FEE_ACCOUNT_CREATE "fee_account_create"
#define SECTION_FEE_DEFAULT "fee_default"
#define SECTION_FEE_NICKNAME_CREATE "fee_nickname_create"
#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_RPC_ALLOW_REMOTE "rpc_allow_remote"
#define SECTION_VALIDATION_SEED "validation_seed"
#define SECTION_PEER_SSL_CIPHER_LIST "peer_ssl_cipher_list"
#define SECTION_PEER_SCAN_INTERVAL_MIN "peer_scan_interval_min"
#define SECTION_PEER_START_MAX "peer_start_max"
#define SECTION_PEER_CONNECT_LOW_WATER "peer_connect_low_water"
#define SECTION_NETWORK_QUORUM "network_quorum"
#define SECTION_UNL_DEFAULT "unl_default"
#define SECTION_VALIDATION_QUORUM "validation_quorum"
#define SECTION_FEE_ACCOUNT_CREATE "fee_account_create"
#define SECTION_FEE_NICKNAME_CREATE "fee_nickname_create"
#define SECTION_FEE_DEFAULT "fee_default"
#define SECTION_ACCOUNT_PROBE_MAX "account_probe_max"
#define SECTION_VALIDATION_SEED "validation_seed"
#define SECTION_WEBSOCKET_IP "websocket_ip"
#define SECTION_WEBSOCKET_PORT "websocket_port"
// Fees are in XNB.
#define DEFAULT_FEE_ACCOUNT_CREATE 1000
#define DEFAULT_FEE_NICKNAME_CREATE 1000
#define DEFAULT_FEE_DEFAULT 100
Config theConfig;
@@ -169,7 +173,7 @@ void Config::load()
(void) sectionSingleB(secConfig, SECTION_PEER_IP, PEER_IP);
if (sectionSingleB(secConfig, SECTION_PEER_PORT, strTemp))
PEER_PORT = boost::lexical_cast<int>(strTemp);
PEER_PORT = boost::lexical_cast<int>(strTemp);
(void) sectionSingleB(secConfig, SECTION_RPC_IP, RPC_IP);
@@ -177,7 +181,12 @@ void Config::load()
RPC_PORT = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_RPC_ALLOW_REMOTE, strTemp))
RPC_ALLOW_REMOTE = boost::lexical_cast<bool>(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);
if (sectionSingleB(secConfig, SECTION_VALIDATION_SEED, strTemp))
VALIDATION_SEED.setSeedGeneric(strTemp);
@@ -187,28 +196,31 @@ void Config::load()
PEER_SCAN_INTERVAL_MIN = MAX(60, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_PEER_START_MAX, strTemp))
PEER_START_MAX = MAX(1, boost::lexical_cast<int>(strTemp));
PEER_START_MAX = MAX(1, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_PEER_CONNECT_LOW_WATER, strTemp))
PEER_CONNECT_LOW_WATER = MAX(1, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_NETWORK_QUORUM, strTemp))
NETWORK_QUORUM = MAX(0, boost::lexical_cast<int>(strTemp));
NETWORK_QUORUM = MAX(0, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_VALIDATION_QUORUM, strTemp))
VALIDATION_QUORUM = MAX(0, boost::lexical_cast<int>(strTemp));
VALIDATION_QUORUM = MAX(0, boost::lexical_cast<int>(strTemp));
if (sectionSingleB(secConfig, SECTION_FEE_ACCOUNT_CREATE, strTemp))
FEE_ACCOUNT_CREATE = boost::lexical_cast<int>(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);
FEE_NICKNAME_CREATE = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_FEE_DEFAULT, strTemp))
FEE_DEFAULT = boost::lexical_cast<int>(strTemp);
FEE_DEFAULT = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp))
ACCOUNT_PROBE_MAX = boost::lexical_cast<int>(strTemp);
ACCOUNT_PROBE_MAX = boost::lexical_cast<int>(strTemp);
if (sectionSingleB(secConfig, SECTION_UNL_DEFAULT, strTemp))
UNL_DEFAULT = strTemp;
}
}
}

View File

@@ -44,32 +44,31 @@ public:
boost::filesystem::path CONFIG_FILE;
boost::filesystem::path CONFIG_DIR;
boost::filesystem::path DATA_DIR;
boost::filesystem::path UNL_DEFAULT;
// 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;
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;
// Note: The following parameters do not relate to the UNL or trust at all
int NETWORK_QUORUM; // Minimum number of nodes to consider the network present
int VALIDATION_QUORUM; // Minimum validations to consider ledger authoritative
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;
int PEER_PORT;
int NUMBER_CONNECTIONS;
std::string PEER_SSL_CIPHER_LIST;
int PEER_SCAN_INTERVAL_MIN;
int PEER_START_MAX;
int PEER_CONNECT_LOW_WATER;
int PEER_SCAN_INTERVAL_MIN;
int PEER_START_MAX;
int PEER_CONNECT_LOW_WATER;
// bool NODE_INBOUND; // We accept inbound connections
// bool NODE_DATABASE; // We offer historical data services
// bool NODE_PUBLIC; // We do not attempt to hide our identity
// bool NODE_DUMB; // We are a 'dumb' client
// bool NODE_SMART; // We offer services to 'dumb' clients
// Client networking parameters
std::string WEBSOCKET_IP;
int WEBSOCKET_PORT;
// RPC parameters
std::string RPC_IP;
@@ -95,4 +94,5 @@ public:
extern Config theConfig;
#endif
// vim:ts=4

View File

@@ -81,26 +81,27 @@ bool ConnectionPool::savePeer(const std::string& strIp, int iPort,char code)
return false;
}
// <-- 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);
pipPeer ipPeer;
vstrIpPort.reserve(mIpMap.size());
BOOST_FOREACH(ipPeer, mIpMap)
BOOST_FOREACH(pipPeer ipPeer, mIpMap)
{
std::string& strIp = ipPeer.first.first;
int iPort = ipPeer.first.second;
const std::string& strIp = ipPeer.first.first;
int iPort = ipPeer.first.second;
vstrIpPort.push_back(db->escape(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());

View File

@@ -117,6 +117,11 @@ const char *WalletDBInit[] = {
// Table of PublicKeys user has asked to trust.
// Fetches are made to the CAS. This gets the newcoin.txt so even validators without a web server can publish a newcoin.txt.
// Source:
// 'M' = Manually added. : 1500
// 'V' = validators.txt : 1000
// 'W' = Web browsing. : 200
// 'R' = Referral : 0
// Next:
// Time of next fetch attempt.
// Scan:
@@ -129,6 +134,7 @@ const char *WalletDBInit[] = {
// User supplied comment.
"CREATE TABLE SeedNodes ( \
PublicKey CHARACTER(53) PRIMARY KEY NOT NULL, \
Source CHARACTER(1) NOT NULL, \
Next DATETIME, \
Scan DATETIME, \
Fetch DATETIME, \
@@ -139,7 +145,7 @@ const char *WalletDBInit[] = {
// Allow us to easily find the next SeedNode to fetch.
"CREATE INDEX SeedNodeNext ON SeedNodes (Next);",
// Nodes we trust not grossly consipire. Derived from SeedDomains, SeedNodes, and ValidatorReferrals.
// Nodes we trust to not grossly collude against us. Derived from SeedDomains, SeedNodes, and ValidatorReferrals.
//
// Score:
// Computed trust score. Higher is better.

View File

@@ -300,7 +300,6 @@ void Ledger::saveAcceptedLedger(Ledger::pointer ledger)
"UPDATE Transactions SET LedgerSeq = '%d', Status = '%c' WHERE TransID = '%s';") %
ledger->getLedgerSeq() % TXN_SQL_VALIDATED % txn.getTransactionID().GetHex()));
}
// FIXME: If above updates no rows, modify seq/status (upsert)
}
db->executeSQL("COMMIT TRANSACTION;");
}

View File

@@ -363,7 +363,7 @@ int LedgerConsensus::timerEntry()
case lcsCUTOFF: return stateCutoff(sinceClose);
case lcsFINISHED: return stateFinished(sinceClose);
case lcsACCEPTED: return stateAccepted(sinceClose);
case lcsABORTED: return stateAccepted(sinceClose);
case lcsABORTED: return 1;
}
assert(false);
return 1;
@@ -702,7 +702,6 @@ void LedgerConsensus::accept(SHAMap::pointer set)
#endif
ScopedLock sl = theApp->getMasterLedger().getLock();
applyTransactions(theApp->getMasterLedger().getCurrentLedger()->peekTransactionMap(), newOL, failedTransactions);
theApp->getMasterLedger().pushLedger(newLCL, newOL);
mState = lcsACCEPTED;

View File

@@ -1,5 +1,4 @@
#include <fstream>
#include <iostream>
#include <boost/bind.hpp>
@@ -25,14 +24,6 @@
#include "NicknameState.h"
#include "utils.h"
#define VALIDATORS_FETCH_SECONDS 30
#define VALIDATORS_FILE_PATH "/" VALIDATORS_FILE_NAME
#define VALIDATORS_FILE_BYTES_MAX (50 << 10)
/*
Just read from wire until the entire request is in.
*/
RPCServer::RPCServer(boost::asio::io_service& io_service , NetworkOPs* nopNetwork)
: mNetOps(nopNetwork), mSocket(io_service)
{
@@ -1462,11 +1453,11 @@ Json::Value RPCServer::doUnlAdd(Json::Value& params)
std::string strNode = params[0u].asString();
std::string strComment = (params.size() == 2) ? params[1u].asString() : "";
NewcoinAddress nodePublic;
NewcoinAddress naNodePublic;
if (nodePublic.setNodePublic(strNode))
if (naNodePublic.setNodePublic(strNode))
{
theApp->getUNL().nodeAddPublic(nodePublic, strComment);
theApp->getUNL().nodeAddPublic(naNodePublic, UniqueNodeList::vsManual, strComment);
return "adding node by public key";
}
@@ -1476,8 +1467,6 @@ Json::Value RPCServer::doUnlAdd(Json::Value& params)
return "adding node by domain";
}
return "invalid params";
}
// validation_create [<pass_phrase>|<seed>|<seed_key>]
@@ -1906,110 +1895,66 @@ Json::Value RPCServer::doWalletSeed(Json::Value& params)
}
}
void RPCServer::validatorsResponse(const boost::system::error_code& err, std::string strResponse)
// unl_delete <domain>|<public_key>
Json::Value RPCServer::doUnlDelete(Json::Value& params)
{
std::cerr << "Fetch '" VALIDATORS_FILE_NAME "' complete." << std::endl;
if (!err)
{
theApp->getUNL().nodeDefault(strResponse);
}
else
{
std::cerr << "Error: " << err.message() << std::endl;
}
}
// Populate the UNL from a validators.txt file.
Json::Value RPCServer::doUnlDefault(Json::Value& params) {
if (!params.size() || (1==params.size() && !params[0u].compare("network")))
{
bool bNetwork = 1 == params.size();
std::string strValidators;
if (!bNetwork)
{
std::ifstream ifsDefault(VALIDATORS_FILE_NAME, std::ios::in);
if (!ifsDefault)
{
std::cerr << "Failed to open '" VALIDATORS_FILE_NAME "'." << std::endl;
bNetwork = true;
}
else
{
strValidators.assign((std::istreambuf_iterator<char>(ifsDefault)),
std::istreambuf_iterator<char>());
if (ifsDefault.bad())
{
std::cerr << "Failed to read '" VALIDATORS_FILE_NAME "'." << std::endl;
bNetwork = true;
}
}
}
if (bNetwork)
{
HttpsClient::httpsGet(
theApp->getIOService(),
VALIDATORS_SITE,
443,
VALIDATORS_FILE_PATH,
VALIDATORS_FILE_BYTES_MAX,
boost::posix_time::seconds(VALIDATORS_FETCH_SECONDS),
boost::bind(&RPCServer::validatorsResponse, this, _1, _2));
return "fetching " VALIDATORS_FILE_NAME;
}
else
{
theApp->getUNL().nodeDefault(strValidators);
return "processing " VALIDATORS_FILE_NAME;
}
}
else
return RPCError(rpcINVALID_PARAMS);
}
// unl_delete <public_key>
Json::Value RPCServer::doUnlDelete(Json::Value& params)
{
std::string strNodePublic = params[0u].asString();
std::string strNode = params[0u].asString();
NewcoinAddress naNodePublic;
if (naNodePublic.setNodePublic(strNodePublic))
if (naNodePublic.setNodePublic(strNode))
{
theApp->getUNL().nodeRemove(naNodePublic);
theApp->getUNL().nodeRemovePublic(naNodePublic);
return "removing node";
return "removing node by public key";
}
else
{
return "invalid public key";
theApp->getUNL().nodeRemoveDomain(strNode);
return "removing node by domain";
}
}
Json::Value RPCServer::doUnlList(Json::Value& params)
Json::Value RPCServer::doUnlList(Json::Value& params)
{
Json::Value obj(Json::objectValue);
obj["unl"]=theApp->getUNL().getUnlJson();
return obj;
}
// Populate the UNL from a local validators.txt file.
Json::Value RPCServer::doUnlLoad(Json::Value& params)
{
if (!theApp->getUNL().nodeLoad())
{
return RPCError(rpcLOAD_FAILED);
}
return "loading";
}
// Populate the UNL from newcoin.org's validators.txt file.
Json::Value RPCServer::doUnlNetwork(Json::Value& params)
{
theApp->getUNL().nodeNetwork();
return "fetching";
}
// unl_reset
Json::Value RPCServer::doUnlReset(Json::Value& params) {
Json::Value RPCServer::doUnlReset(Json::Value& params)
{
theApp->getUNL().nodeReset();
return "removing nodes";
}
// unl_score
Json::Value RPCServer::doUnlScore(Json::Value& params) {
Json::Value RPCServer::doUnlScore(Json::Value& params)
{
theApp->getUNL().nodeScore();
return "scoring requested";
@@ -2055,9 +2000,10 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params
{ "tx", &RPCServer::doTx, 1, 1, },
{ "unl_add", &RPCServer::doUnlAdd, 1, 2, },
{ "unl_default", &RPCServer::doUnlDefault, 0, 1, },
{ "unl_delete", &RPCServer::doUnlDelete, 1, 1, },
{ "unl_list", &RPCServer::doUnlList, 0, 0, },
{ "unl_load", &RPCServer::doUnlLoad, 0, 0, },
{ "unl_network", &RPCServer::doUnlNetwork, 0, 0, },
{ "unl_reset", &RPCServer::doUnlReset, 0, 0, },
{ "unl_score", &RPCServer::doUnlScore, 0, 0, },

View File

@@ -17,6 +17,9 @@ public:
enum {
rpcSUCCESS,
// Misc failure
rpcLOAD_FAILED,
// Networking
rpcNO_CLOSED,
rpcNO_CURRENT,
@@ -40,7 +43,7 @@ public:
rpcINVALID_PARAMS,
rpcUNKNOWN_COMMAND,
// Bad paramater
// Bad parameter
rpcACT_MALFORMED,
rpcBAD_SEED,
rpcDST_ACT_MALFORMED,
@@ -131,10 +134,11 @@ private:
Json::Value doTx(Json::Value& params);
Json::Value doUnlAdd(Json::Value& params);
Json::Value doUnlDefault(Json::Value& params);
Json::Value doUnlDelete(Json::Value& params);
Json::Value doUnlFetch(Json::Value& params);
Json::Value doUnlList(Json::Value& params);
Json::Value doUnlLoad(Json::Value& params);
Json::Value doUnlNetwork(Json::Value& params);
Json::Value doUnlReset(Json::Value& params);
Json::Value doUnlScore(Json::Value& params);
@@ -151,8 +155,6 @@ private:
Json::Value doWalletUnlock(Json::Value& params);
Json::Value doWalletVerify(Json::Value& params);
void validatorsResponse(const boost::system::error_code& err, std::string strResponse);
public:
typedef boost::shared_ptr<RPCServer> pointer;

View File

@@ -513,7 +513,7 @@ bool Transaction::save()
if ((mStatus == INVALID) || (mStatus == REMOVED)) return false;
char status;
switch(mStatus)
switch (mStatus)
{
case NEW: status = TXN_SQL_NEW; break;
case INCLUDED: status = TXN_SQL_INCLUDED; break;
@@ -523,8 +523,6 @@ bool Transaction::save()
default: status = TXN_SQL_UNKNOWN;
}
// FIXME: This needs to check if the transaction is already there and not
// de-confirm it
Database *db = theApp->getTxnDB()->getDB();
ScopedLock dbLock = theApp->getTxnDB()->getDBLock();
return db->executeSQL(mTransaction->getSQLInsertHeader() + mTransaction->getSQL(getLedger(), status) + ";");

View File

@@ -9,6 +9,7 @@
#include "UniqueNodeList.h"
#include "utils.h"
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
@@ -16,6 +17,13 @@
#include <boost/mem_fn.hpp>
#include <boost/regex.hpp>
#include <fstream>
#include <iostream>
#define VALIDATORS_FETCH_SECONDS 30
#define VALIDATORS_FILE_PATH "/" VALIDATORS_FILE_NAME
#define VALIDATORS_FILE_BYTES_MAX (50 << 10)
// Gather string constants.
#define SECTION_CURRENCIES "currencies"
#define SECTION_DOMAIN "domain"
@@ -26,6 +34,7 @@
#define SECTION_VALIDATORS_URL "validators_url"
// Limit pollution of database.
// YYY Move to config file.
#define REFERRAL_VALIDATORS_MAX 50
#define REFERRAL_IPS_MAX 50
@@ -90,13 +99,10 @@ void UniqueNodeList::trustedLoad()
mUNL.clear();
// XXX Needs to limit by quanity and quality.
SQL_FOREACH(db, "SELECT PublicKey FROM TrustedNodes WHERE Score != 0;")
{
std::string strPublicKey;
db->getStr("PublicKey", strPublicKey);
mUNL.insert(strPublicKey);
mUNL.insert(db->getStrBinary("PublicKey"));
}
}
@@ -169,12 +175,14 @@ void UniqueNodeList::scoreCompute()
{
strIndex umPulicIdx; // Map of public key to index.
strIndex umDomainIdx; // Map of domain to index.
std::vector<scoreNode> vsnNodes;
std::vector<scoreNode> vsnNodes; // Index to scoring node.
Database* db=theApp->getWalletDB()->getDB();
std::string strSql;
// For each entry in SeedDomains with a PublicKey:
// - Add an entry in umPulicIdx, umDomainIdx, and vsnNodes.
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
@@ -186,32 +194,91 @@ void UniqueNodeList::scoreCompute()
}
else
{
std::string strDomain;
std::string strPublicKey;
std::string strSource;
std::string strDomain = db->getStrBinary("Domain");
std::string strPublicKey = db->getStrBinary("PublicKey");
std::string strSource = db->getStrBinary("Source");
int iScore = iSourceScore(static_cast<validatorSource>(strSource[0]));
strIndex::iterator siOld = umPulicIdx.find(strPublicKey);
db->getStr("Domain", strDomain);
db->getStr("PublicKey", strPublicKey);
db->getStr("Source", strSource);
if (siOld == umPulicIdx.end())
{
// New node
int iNode = vsnNodes.size();
umPulicIdx[strPublicKey] = iNode;
umDomainIdx[strDomain] = iNode;
scoreNode snCurrent;
snCurrent.strValidator = strPublicKey;
snCurrent.iScore = iScore;
snCurrent.iRoundSeed = snCurrent.iScore;
snCurrent.iRoundScore = 0;
snCurrent.iSeen = -1;
vsnNodes.push_back(snCurrent);
}
else
{
scoreNode& snOld = vsnNodes[siOld->second];
if (snOld.iScore < iScore)
{
// Update old node
snOld.iScore = iScore;
snOld.iRoundSeed = snOld.iScore;
}
}
}
}
}
// For each entry in SeedNodes:
// - Add an entry in umPulicIdx, umDomainIdx, and vsnNodes.
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
SQL_FOREACH(db, "SELECT PublicKey,Source FROM SeedNodes;")
{
std::string strPublicKey = db->getStrBinary("PublicKey");
std::string strSource = db->getStrBinary("Source");
int iScore = iSourceScore(static_cast<validatorSource>(strSource[0]));
strIndex::iterator siOld = umPulicIdx.find(strPublicKey);
if (siOld == umPulicIdx.end())
{
// New node
int iNode = vsnNodes.size();
umPulicIdx[strPublicKey] = iNode;
umDomainIdx[strDomain] = iNode;
scoreNode snCurrent;
snCurrent.strValidator = strPublicKey;
snCurrent.iScore = iSourceScore(static_cast<validatorSource>(strSource[0]));
snCurrent.iScore = iScore;
snCurrent.iRoundSeed = snCurrent.iScore;
snCurrent.iRoundScore = 0;
snCurrent.iSeen = -1;
vsnNodes.push_back(snCurrent);
}
else
{
scoreNode& snOld = vsnNodes[siOld->second];
if (snOld.iScore < iScore)
{
// Update old node
snOld.iScore = iScore;
snOld.iRoundSeed = snOld.iScore;
}
}
}
}
// For debugging, print out initial scores.
BOOST_FOREACH(scoreNode& sn, vsnNodes)
{
std::cerr << str(boost::format("%s| %d, %d, %d")
@@ -222,11 +289,10 @@ void UniqueNodeList::scoreCompute()
<< std::endl;
}
// XXX When we have a CAS do SeedNodes here.
// std::cerr << str(boost::format("vsnNodes.size=%d") % vsnNodes.size()) << std::endl;
// Step through growing list of nodes adding each validation list.
// - Each validator may have provided referals. Add those referals as validators.
for (int iNode=0; iNode != vsnNodes.size(); iNode++)
{
scoreNode& sn = vsnNodes[iNode];
@@ -238,11 +304,9 @@ void UniqueNodeList::scoreCompute()
SQL_FOREACH(db, str(boost::format("SELECT Referral FROM ValidatorReferrals WHERE Validator=%s ORDER BY Entry;")
% db->escape(strValidator)))
{
std::string strReferral;
std::string strReferral = db->getStrBinary("Referral");
int iReferral;
db->getStr("Referral", strReferral);
strIndex::iterator itEntry;
NewcoinAddress na;
@@ -335,11 +399,7 @@ void UniqueNodeList::scoreCompute()
SQL_FOREACH(db, str(boost::format("SELECT PublicKey,Seen FROM TrustedNodes WHERE PublicKey IN (%s);")
% strJoin(vstrPublicKeys.begin(), vstrPublicKeys.end(), ",")))
{
std::string strPublicKey;
db->getStr("PublicKey", strPublicKey);
vsnNodes[umPulicIdx[strPublicKey]].iSeen = db->getNull("Seen") ? -1 : db->getInt("Seen");
vsnNodes[umPulicIdx[db->getStrBinary("PublicKey")]].iSeen = db->getNull("Seen") ? -1 : db->getInt("Seen");
}
}
@@ -358,7 +418,7 @@ void UniqueNodeList::scoreCompute()
std::string strSeen = sn.iSeen >= 0 ? str(boost::format("%d") % sn.iSeen) : "NULL";
vstrValues[iNode] = str(boost::format("(%s,%s,%s)")
% db->escape(sn.strValidator)
% sqlEscape(sn.strValidator)
% sn.iScore
% strSeen);
@@ -372,6 +432,7 @@ void UniqueNodeList::scoreCompute()
{
ScopedLock sl(mUNLLock);
// XXX Should limit to scores above a certain minimum and limit to a certain number.
mUNL.swap(usUNL);
}
@@ -387,11 +448,7 @@ void UniqueNodeList::scoreCompute()
// For every IpReferral add a score for the IP and PORT.
SQL_FOREACH(db, "SELECT Validator,COUNT(*) AS Count FROM IpReferrals GROUP BY Validator;")
{
std::string strValidator;
db->getStr("Validator", strValidator);
umValidators[strValidator] = db->getInt("Count");
umValidators[db->getStrBinary("Validator")] = db->getInt("Count");
// std::cerr << strValidator << ":" << db->getInt("Count") << std::endl;
}
@@ -418,13 +475,11 @@ void UniqueNodeList::scoreCompute()
% db->escape(strValidator)))
{
score iPoints = iBase * (iEntries - iEntry) / iEntries;
std::string strIP;
int iPort;
db->getStr("IP", strIP);
iPort = db->getNull("Port") ? -1 : db->getInt("Port");
std::pair< std::string, int> ep = std::make_pair(strIP, iPort);
std::pair< std::string, int> ep = std::make_pair(db->getStrBinary("IP"), iPort);
epScore::iterator itEp = umScore.find(ep);
@@ -491,6 +546,7 @@ void UniqueNodeList::scoreTimerHandler(const boost::system::error_code& err)
}
// Start a timer to update scores.
// <-- bNow: true, to force scoring for debugging.
void UniqueNodeList::scoreNext(bool bNow)
{
// std::cerr << str(boost::format("scoreNext: mtpFetchUpdated=%s mtpScoreStart=%s mtpScoreUpdated=%s mtpScoreNext=%s") % mtpFetchUpdated % mtpScoreStart % mtpScoreUpdated % mtpScoreNext) << std::endl;
@@ -534,6 +590,7 @@ void UniqueNodeList::fetchFinish()
fetchNext();
}
// Called when we need to update scores.
void UniqueNodeList::fetchDirty()
{
// Note update.
@@ -631,11 +688,14 @@ void UniqueNodeList::processIps(const std::string& strSite, const NewcoinAddress
}
// Persist ValidatorReferrals.
void UniqueNodeList::processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const NewcoinAddress& naNodePublic, section::mapped_type* pmtVecStrValidators)
// --> strSite: source site for display
// --> strValidatorsSrc: source details for display
// --> naNodePublic: remote source public key - not valid for local
// --> vsWhy: reason for adding validator to SeedDomains or SeedNodes.
void UniqueNodeList::processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const NewcoinAddress& naNodePublic, validatorSource vsWhy, section::mapped_type* pmtVecStrValidators)
{
Database* db=theApp->getWalletDB()->getDB();
std::string strEscNodePublic = db->escape(naNodePublic.humanNodePublic());
Database* db = theApp->getWalletDB()->getDB();
std::string strNodePublic = naNodePublic.isValid() ? naNodePublic.humanNodePublic() : "local";
std::cerr
<< str(boost::format("Validator: '%s' : '%s' : processing %d validators.")
@@ -647,7 +707,8 @@ void UniqueNodeList::processValidators(const std::string& strSite, const std::st
// Remove all current Validator's entries in ValidatorReferrals
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(str(boost::format("DELETE FROM ValidatorReferrals WHERE Validator=%s;") % strEscNodePublic));
db->executeSQL(str(boost::format("DELETE FROM ValidatorReferrals WHERE Validator='%s';") % strNodePublic));
// XXX Check result.
}
@@ -655,38 +716,68 @@ void UniqueNodeList::processValidators(const std::string& strSite, const std::st
if (pmtVecStrValidators && pmtVecStrValidators->size()) {
std::vector<std::string> vstrValues;
vstrValues.resize(MIN(pmtVecStrValidators->size(), REFERRAL_VALIDATORS_MAX));
vstrValues.reserve(MIN(pmtVecStrValidators->size(), REFERRAL_VALIDATORS_MAX));
int i = 0;
int iValues = 0;
BOOST_FOREACH(std::string strReferral, *pmtVecStrValidators)
{
if (i == REFERRAL_VALIDATORS_MAX)
if (iValues == REFERRAL_VALIDATORS_MAX)
break;
vstrValues[i] = str(boost::format("(%s,%d,%s)") % strEscNodePublic % i % db->escape(strReferral));
i++;
boost::smatch smMatch;
std::string strIP;
NewcoinAddress naValidator;
// domain comment?
// public_key comment?
static boost::regex reReferral("\\`\\s*(\\S+)(?:\\s+(.+))?\\s*\\'");
if (naValidator.setNodePublic(strReferral))
if (!boost::regex_match(strReferral, smMatch, reReferral))
{
// A public key.
// XXX Schedule for CAS lookup.
std::cerr
<< str(boost::format("Validator: '%s' ["SECTION_VALIDATORS"]: rejecting '%s'")
% strSite % strReferral)
<< std::endl;
}
else
{
// A domain: need to look it up.
nodeAddDomain(strReferral, vsReferral);
std::string strRefered = smMatch[1];
std::string strComment = smMatch[2];
NewcoinAddress naValidator;
// std::cerr << str(boost::format("Validator: '%s' : '%s'") % strRefered % strComment) << std::endl;
if (naValidator.setNodePublic(strRefered))
{
// A public key.
// XXX Schedule for CAS lookup.
nodeAddPublic(naValidator, vsWhy, strComment);
if (naNodePublic.isValid())
vstrValues.push_back(str(boost::format("('%s',%d,'%s')") % strNodePublic % iValues % naValidator.humanNodePublic()));
}
else
{
// A domain: need to look it up.
nodeAddDomain(strRefered, vsWhy, strComment);
if (naNodePublic.isValid())
vstrValues.push_back(str(boost::format("('%s',%d,%s)") % strNodePublic % iValues % sqlEscape(strRefered)));
}
iValues++;
}
}
std::string strSql = str(boost::format("INSERT INTO ValidatorReferrals (Validator,Entry,Referral) VALUES %s;")
% strJoin(vstrValues.begin(), vstrValues.end(), ","));
if (!vstrValues.empty())
{
std::string strSql = str(boost::format("INSERT INTO ValidatorReferrals (Validator,Entry,Referral) VALUES %s;")
% strJoin(vstrValues.begin(), vstrValues.end(), ","));
ScopedLock sl(theApp->getWalletDB()->getDBLock());
ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(strSql);
// XXX Check result.
db->executeSQL(strSql);
// XXX Check result.
}
}
fetchDirty();
@@ -707,7 +798,7 @@ void UniqueNodeList::responseIps(const std::string& strSite, const NewcoinAddres
// Process section [ips_url].
// If we have a section with a single entry, fetch the url and process it.
void UniqueNodeList::getIpsUrl(NewcoinAddress naNodePublic, section secSite)
void UniqueNodeList::getIpsUrl(const NewcoinAddress& naNodePublic, section secSite)
{
std::string strIpsUrl;
std::string strDomain;
@@ -733,20 +824,20 @@ void UniqueNodeList::getIpsUrl(NewcoinAddress naNodePublic, section secSite)
}
// Given a section with validators, parse and persist it.
void UniqueNodeList::responseValidators(const std::string& strValidatorsUrl, NewcoinAddress naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string strValidatorsFile)
void UniqueNodeList::responseValidators(const std::string& strValidatorsUrl, const NewcoinAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string strValidatorsFile)
{
if (!err)
{
section secFile = ParseSection(strValidatorsFile, true);
processValidators(strSite, strValidatorsUrl, naNodePublic, sectionEntries(secFile, SECTION_VALIDATORS));
processValidators(strSite, strValidatorsUrl, naNodePublic, vsValidator, sectionEntries(secFile, SECTION_VALIDATORS));
}
getIpsUrl(naNodePublic, secSite);
}
// Process section [validators_url].
void UniqueNodeList::getValidatorsUrl(NewcoinAddress naNodePublic, section secSite)
void UniqueNodeList::getValidatorsUrl(const NewcoinAddress& naNodePublic, section secSite)
{
std::string strValidatorsUrl;
std::string strDomain;
@@ -772,12 +863,12 @@ void UniqueNodeList::getValidatorsUrl(NewcoinAddress naNodePublic, section secSi
}
// Process a newcoin.txt.
void UniqueNodeList::processFile(const std::string strDomain, NewcoinAddress naNodePublic, section secSite)
void UniqueNodeList::processFile(const std::string strDomain, const NewcoinAddress& naNodePublic, section secSite)
{
//
// Process Validators
//
processValidators(strDomain, NODE_FILE_NAME, naNodePublic, sectionEntries(secSite, SECTION_VALIDATORS));
processValidators(strDomain, NODE_FILE_NAME, naNodePublic, vsReferral, sectionEntries(secSite, SECTION_VALIDATORS));
//
// Process ips
@@ -981,7 +1072,7 @@ void UniqueNodeList::fetchNext()
tpNow = boost::posix_time::second_clock::universal_time();
std::cerr << str(boost::format("fetchNext: iNext=%s tpNext=%s tpNow=%s") % iNext % tpNext % tpNow) << std::endl;
db->getStr("Domain", strDomain);
strDomain = db->getStrBinary("Domain");
db->endIterRows();
}
@@ -1057,48 +1148,7 @@ int UniqueNodeList::iSourceScore(validatorSource vsWhy)
return iScore;
}
// Queue a domain for a single attempt fetch a newcoin.txt.
// --> strComment: only used on vsManual
// YYY As a lot of these may happen at once, would be nice to wrap multiple calls in a transaction.
void UniqueNodeList::nodeAddDomain(const std::string& strDomain, validatorSource vsWhy, std::string strComment)
{
// YYY Would be best to verify strDomain is a valid domain.
// std::cerr << str(boost::format("nodeAddDomain: '%s' %c '%s'")
// % strDomain
// % vsWhy
// % strComment) << std::endl;
seedDomain sdCurrent;
bool bFound = getSeedDomains(strDomain, sdCurrent);
bool bChanged = false;
if (!bFound)
{
sdCurrent.strDomain = strDomain;
sdCurrent.tpNext = boost::posix_time::second_clock::universal_time();
}
// Promote source, if needed.
if (!bFound || iSourceScore(vsWhy) >= iSourceScore(sdCurrent.vsSource))
{
sdCurrent.vsSource = vsWhy;
bChanged = true;
}
if (vsManual == vsWhy)
{
// A manual add forces immediate scan.
sdCurrent.tpNext = boost::posix_time::second_clock::universal_time();
sdCurrent.strComment = strComment;
bChanged = true;
}
if (bChanged)
setSeedDomains(sdCurrent, true);
}
// Retrieve a SeedDomain for DB.
// Retrieve a SeedDomain from DB.
bool UniqueNodeList::getSeedDomains(const std::string& strDomain, seedDomain& dstSeedDomain)
{
bool bResult;
@@ -1113,13 +1163,12 @@ bool UniqueNodeList::getSeedDomains(const std::string& strDomain, seedDomain& ds
if (bResult)
{
std::string strPublicKey;
std::string strSource;
int iNext;
int iScan;
int iFetch;
std::string strSha256;
db->getStr("Domain", dstSeedDomain.strDomain);
dstSeedDomain.strDomain = db->getStrBinary("Domain");
if (!db->getNull("PublicKey") && db->getStr("PublicKey", strPublicKey))
{
@@ -1130,14 +1179,16 @@ bool UniqueNodeList::getSeedDomains(const std::string& strDomain, seedDomain& ds
dstSeedDomain.naPublicKey.clear();
}
db->getStr("Source", strSource);
std::string strSource = db->getStrBinary("Source");
dstSeedDomain.vsSource = static_cast<validatorSource>(strSource[0]);
iNext = db->getInt("Next");
dstSeedDomain.tpNext = ptFromSeconds(iNext);
iScan = db->getInt("Scan");
dstSeedDomain.tpScan = ptFromSeconds(iScan);
iFetch = db->getInt("Fetch");
dstSeedDomain.tpFetch = ptFromSeconds(iFetch);
if (!db->getNull("Sha256") && db->getStr("Sha256", strSha256))
{
dstSeedDomain.iSha256.SetHex(strSha256);
@@ -1146,7 +1197,7 @@ bool UniqueNodeList::getSeedDomains(const std::string& strDomain, seedDomain& ds
{
dstSeedDomain.iSha256.zero();
}
db->getStr("Comment", dstSeedDomain.strComment);
dstSeedDomain.strComment = db->getStrBinary("Comment");
db->endIterRows();
}
@@ -1191,68 +1242,227 @@ void UniqueNodeList::setSeedDomains(const seedDomain& sdSource, bool bNext)
}
}
// Add a trusted node. Called by RPC or other source.
// XXX Broken should operate on seeds.
void UniqueNodeList::nodeAddPublic(const NewcoinAddress& naNodePublic, const std::string& strComment)
// Queue a domain for a single attempt fetch a newcoin.txt.
// --> strComment: only used on vsManual
// YYY As a lot of these may happen at once, would be nice to wrap multiple calls in a transaction.
void UniqueNodeList::nodeAddDomain(std::string strDomain, validatorSource vsWhy, const std::string& strComment)
{
std::string strPublicKey = naNodePublic.humanNodePublic();
boost::trim(strDomain);
boost::to_lower(strDomain);
// YYY Would be best to verify strDomain is a valid domain.
// std::cerr << str(boost::format("nodeAddDomain: '%s' %c '%s'")
// % strDomain
// % vsWhy
// % strComment) << std::endl;
seedDomain sdCurrent;
bool bFound = getSeedDomains(strDomain, sdCurrent);
bool bChanged = false;
if (!bFound)
{
Database* db=theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock());
sdCurrent.strDomain = strDomain;
sdCurrent.tpNext = boost::posix_time::second_clock::universal_time();
}
if( db->executeSQL(str(boost::format("SELECT count(*) from TrustedNodes where PublicKey=%s;") % db->escape(strPublicKey))) &&
db->startIterRows() && db->getInt(0)==1 )
{ // exists. update the comment
db->executeSQL(str(boost::format("UPDATE TrustedNodes set Comment=%s where PublicKey=%s;") % db->escape(strComment) % db->escape(strPublicKey) ));
}else
{ // new node
db->executeSQL(str(boost::format("INSERT INTO TrustedNodes (PublicKey,Comment) values (%s,%s);")
% db->escape(strPublicKey) % db->escape(strComment)));
// Promote source, if needed.
if (!bFound || iSourceScore(vsWhy) >= iSourceScore(sdCurrent.vsSource))
{
sdCurrent.vsSource = vsWhy;
sdCurrent.strComment = strComment;
bChanged = true;
}
if (vsManual == vsWhy)
{
// A manual add forces immediate scan.
sdCurrent.tpNext = boost::posix_time::second_clock::universal_time();
bChanged = true;
}
if (bChanged)
setSeedDomains(sdCurrent, true);
}
// Retrieve a SeedNode from DB.
bool UniqueNodeList::getSeedNodes(const NewcoinAddress& naNodePublic, seedNode& dstSeedNode)
{
bool bResult;
Database* db=theApp->getWalletDB()->getDB();
std::string strSql = str(boost::format("SELECT * FROM SeedNodes WHERE PublicKey='%s';")
% naNodePublic.humanNodePublic());
ScopedLock sl(theApp->getWalletDB()->getDBLock());
bResult = db->executeSQL(strSql) && db->startIterRows();
if (bResult)
{
std::string strPublicKey;
std::string strSource;
int iNext;
int iScan;
int iFetch;
std::string strSha256;
if (!db->getNull("PublicKey") && db->getStr("PublicKey", strPublicKey))
{
dstSeedNode.naPublicKey.setNodePublic(strPublicKey);
}
else
{
dstSeedNode.naPublicKey.clear();
}
strSource = db->getStrBinary("Source");
dstSeedNode.vsSource = static_cast<validatorSource>(strSource[0]);
iNext = db->getInt("Next");
dstSeedNode.tpNext = ptFromSeconds(iNext);
iScan = db->getInt("Scan");
dstSeedNode.tpScan = ptFromSeconds(iScan);
iFetch = db->getInt("Fetch");
dstSeedNode.tpFetch = ptFromSeconds(iFetch);
if (!db->getNull("Sha256") && db->getStr("Sha256", strSha256))
{
dstSeedNode.iSha256.SetHex(strSha256);
}
else
{
dstSeedNode.iSha256.zero();
}
dstSeedNode.strComment = db->getStrBinary("Comment");
db->endIterRows();
}
{
ScopedLock slUNL(mUNLLock);
mUNL.insert(strPublicKey);
}
return bResult;
}
// XXX Broken should operate on seeds.
void UniqueNodeList::nodeRemove(NewcoinAddress naNodePublic)
// Persist a SeedNode.
// <-- bNext: true, to do fetching if needed.
void UniqueNodeList::setSeedNodes(const seedNode& snSource, bool bNext)
{
std::string strPublicKey = naNodePublic.humanNodePublic();
Database* db=theApp->getWalletDB()->getDB();
int iNext = iToSeconds(snSource.tpNext);
int iScan = iToSeconds(snSource.tpScan);
int iFetch = iToSeconds(snSource.tpFetch);
// std::cerr << str(boost::format("setSeedNodes: iNext=%s tpNext=%s") % iNext % sdSource.tpNext) << std::endl;
assert(snSource.naPublicKey.isValid());
std::string strSql = str(boost::format("REPLACE INTO SeedNodes (PublicKey,Source,Next,Scan,Fetch,Sha256,Comment) VALUES ('%s', '%c', %d, %d, %d, '%s', %s);")
% snSource.naPublicKey.humanNodePublic()
% static_cast<char>(snSource.vsSource)
% iNext
% iScan
% iFetch
% snSource.iSha256.GetHex()
% sqlEscape(snSource.strComment)
);
{
ScopedLock sl(theApp->getWalletDB()->getDBLock());
if (!db->executeSQL(strSql))
{
// XXX Check result.
std::cerr << "setSeedNodes: failed." << std::endl;
}
}
#if 0
// YYY When we have a cas schedule lookups similar to this.
if (bNext && (mtpFetchNext.is_not_a_date_time() || mtpFetchNext > snSource.tpNext))
{
// Schedule earlier wake up.
fetchNext();
}
#else
fetchDirty();
#endif
}
// Add a trusted node. Called by RPC or other source.
void UniqueNodeList::nodeAddPublic(const NewcoinAddress& naNodePublic, validatorSource vsWhy, const std::string& strComment)
{
seedNode snCurrent;
bool bFound = getSeedNodes(naNodePublic, snCurrent);
bool bChanged = false;
if (!bFound)
{
snCurrent.naPublicKey = naNodePublic;
snCurrent.tpNext = boost::posix_time::second_clock::universal_time();
}
// Promote source, if needed.
if (!bFound || iSourceScore(vsWhy) >= iSourceScore(snCurrent.vsSource))
{
snCurrent.vsSource = vsWhy;
snCurrent.strComment = strComment;
bChanged = true;
}
if (vsManual == vsWhy)
{
// A manual add forces immediate scan.
snCurrent.tpNext = boost::posix_time::second_clock::universal_time();
bChanged = true;
}
if (bChanged)
setSeedNodes(snCurrent, true);
}
void UniqueNodeList::nodeRemovePublic(const NewcoinAddress& naNodePublic)
{
{
Database* db=theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(str(boost::format("DELETE FROM SeedNodes WHERE PublicKey=%s") % naNodePublic.humanNodePublic()));
}
// YYY Only dirty on successful delete.
fetchDirty();
}
void UniqueNodeList::nodeRemoveDomain(std::string strDomain)
{
boost::trim(strDomain);
boost::to_lower(strDomain);
{
Database* db=theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(str(boost::format("DELETE FROM TrustedNodes where PublicKey=%s") % db->escape(strPublicKey)));
db->executeSQL(str(boost::format("DELETE FROM SeedDomains WHERE Domain=%s") % sqlEscape(strDomain)));
}
{
ScopedLock slUNL(mUNLLock);
mUNL.erase(strPublicKey);
}
// YYY Only dirty on successful delete.
fetchDirty();
}
// XXX Broken should operate on seeds.
void UniqueNodeList::nodeReset()
{
{
Database* db=theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL("DELETE FROM TrustedNodes");
}
{
ScopedLock slUNL(mUNLLock);
mUNL.clear();
// XXX Check results.
db->executeSQL("DELETE FROM SeedDomains");
db->executeSQL("DELETE FROM SeedNodes");
}
fetchDirty();
}
Json::Value UniqueNodeList::getUnlJson()
@@ -1264,16 +1474,10 @@ Json::Value UniqueNodeList::getUnlJson()
ScopedLock sl(theApp->getWalletDB()->getDBLock());
SQL_FOREACH(db, "SELECT * FROM TrustedNodes;")
{
std::string strPublicKey;
std::string strComment;
db->getStr("PublicKey", strPublicKey);
db->getStr("Comment", strComment);
Json::Value node(Json::objectValue);
node["publicKey"] = strPublicKey;
node["comment"] = strComment;
node["publicKey"] = db->getStrBinary("PublicKey");
node["comment"] = db->getStrBinary("Comment");
ret.append(node);
}
@@ -1281,18 +1485,128 @@ Json::Value UniqueNodeList::getUnlJson()
return ret;
}
bool UniqueNodeList::nodeLoad()
{
if (theConfig.UNL_DEFAULT.empty())
{
std::cerr << "UNL_DEFAULT not specified." << std::endl;
return false;
}
if (!boost::filesystem::exists(theConfig.UNL_DEFAULT))
{
std::cerr << str(boost::format("UNL_DEFAULT not found: %s") % theConfig.UNL_DEFAULT) << std::endl;
return false;
}
if (!boost::filesystem::is_regular_file(theConfig.UNL_DEFAULT))
{
std::cerr << str(boost::format("UNL_DEFAULT not regular file: %s") % theConfig.UNL_DEFAULT) << std::endl;
return false;
}
std::ifstream ifsDefault(theConfig.UNL_DEFAULT.native().c_str(), std::ios::in);
if (!ifsDefault)
{
std::cerr << str(boost::format("Failed to open: %s") % theConfig.UNL_DEFAULT) << std::endl;
return false;
}
std::string strValidators;
strValidators.assign((std::istreambuf_iterator<char>(ifsDefault)),
std::istreambuf_iterator<char>());
if (ifsDefault.bad())
{
std::cerr << str(boost::format("Failed to read: %s") % theConfig.UNL_DEFAULT) << std::endl;
return false;
}
nodeDefault(strValidators, theConfig.UNL_DEFAULT.native());
std::cerr << str(boost::format("Processing: %s") % theConfig.UNL_DEFAULT) << std::endl;
return true;
}
void UniqueNodeList::validatorsResponse(const boost::system::error_code& err, std::string strResponse)
{
std::cerr << "Fetch '" VALIDATORS_FILE_NAME "' complete." << std::endl;
if (!err)
{
nodeDefault(strResponse, VALIDATORS_SITE);
}
else
{
std::cerr << "Error: " << err.message() << std::endl;
}
}
void UniqueNodeList::nodeNetwork()
{
HttpsClient::httpsGet(
theApp->getIOService(),
VALIDATORS_SITE,
443,
VALIDATORS_FILE_PATH,
VALIDATORS_FILE_BYTES_MAX,
boost::posix_time::seconds(VALIDATORS_FETCH_SECONDS),
boost::bind(&UniqueNodeList::validatorsResponse, this, _1, _2));
}
void UniqueNodeList::nodeBootstrap()
{
int iDomains = 0;
int iNodes = 0;
{
Database* db=theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock());
if (db->executeSQL(str(boost::format("SELECT COUNT(*) AS Count FROM SeedDomains WHERE Source='%s' OR Source='%c';") % vsManual % vsValidator)) && db->startIterRows())
iDomains = db->getInt("Count");
if (db->executeSQL(str(boost::format("SELECT COUNT(*) AS Count FROM SeedNodes WHERE Source='%s' OR Source='%c';") % vsManual % vsValidator)) && db->startIterRows())
iNodes = db->getInt("Count");
}
bool bLoaded = iDomains || iNodes;
if (!bLoaded && !theConfig.UNL_DEFAULT.empty())
{
std::cerr << "Bootstrapping UNL: loading from file." << std::endl;
bLoaded = nodeLoad();
}
if (!bLoaded)
{
std::cerr << "Bootstrapping UNL: loading from " VALIDATORS_SITE "." << std::endl;
nodeNetwork();
}
}
// Process a validators.txt.
// --> strValidators: a validators.txt
void UniqueNodeList::nodeDefault(const std::string& strValidators) {
// --> strValidators: contents of a validators.txt
void UniqueNodeList::nodeDefault(const std::string& strValidators, const std::string& strSource) {
section secValidators = ParseSection(strValidators, true);
section::mapped_type* pmtEntries = sectionEntries(secValidators, SECTION_VALIDATORS);
if (pmtEntries)
{
BOOST_FOREACH(std::string strValidator, *pmtEntries)
{
nodeAddDomain(strValidator, vsValidator);
}
NewcoinAddress naInvalid; // Don't want a referrer on added entries.
// YYY Unspecified might be bootstrap or rpc command
processValidators("unspecified", strSource, naInvalid, vsValidator, pmtEntries);
}
else
{

View File

@@ -47,6 +47,7 @@ private:
boost::recursive_mutex mUNLLock;
// XXX Make this faster, make this the contents vector unsigned char or raw public key.
// XXX Contents needs to based on score.
boost::unordered_set<std::string> mUNL;
bool miscLoad();
@@ -63,6 +64,16 @@ private:
std::string strComment;
} seedDomain;
typedef struct {
NewcoinAddress naPublicKey;
validatorSource vsSource;
boost::posix_time::ptime tpNext;
boost::posix_time::ptime tpScan;
boost::posix_time::ptime tpFetch;
uint256 iSha256;
std::string strComment;
} seedNode;
// Used to distribute scores.
typedef struct {
int iScore;
@@ -104,35 +115,45 @@ private:
void fetchProcess(std::string strDomain);
void fetchTimerHandler(const boost::system::error_code& err);
void getValidatorsUrl(NewcoinAddress naNodePublic, section secSite);
void getIpsUrl(NewcoinAddress naNodePublic, section secSite);
void getValidatorsUrl(const NewcoinAddress& naNodePublic, section secSite);
void getIpsUrl(const NewcoinAddress& naNodePublic, section secSite);
void responseIps(const std::string& strSite, const NewcoinAddress& naNodePublic, const boost::system::error_code& err, const std::string strIpsFile);
void responseValidators(const std::string& strValidatorsUrl, NewcoinAddress naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string strValidatorsFile);
void responseValidators(const std::string& strValidatorsUrl, const NewcoinAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string strValidatorsFile);
void processIps(const std::string& strSite, const NewcoinAddress& naNodePublic, section::mapped_type* pmtVecStrIps);
void processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const NewcoinAddress& naNodePublic, section::mapped_type* pmtVecStrValidators);
void processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const NewcoinAddress& naNodePublic, validatorSource vsWhy, section::mapped_type* pmtVecStrValidators);
void processFile(const std::string strDomain, NewcoinAddress naNodePublic, section secSite);
void processFile(const std::string strDomain, const NewcoinAddress& naNodePublic, section secSite);
bool getSeedDomains(const std::string& strDomain, seedDomain& dstSeedDomain);
void setSeedDomains(const seedDomain& dstSeedDomain, bool bNext);
bool getSeedNodes(const NewcoinAddress& naNodePublic, seedNode& dstSeedNode);
void setSeedNodes(const seedNode& snSource, bool bNext);
void validatorsResponse(const boost::system::error_code& err, std::string strResponse);
void nodeDefault(const std::string& strValidators, const std::string& strSource);
public:
UniqueNodeList(boost::asio::io_service& io_service);
// Begin processing.
void start();
void nodeAddPublic(const NewcoinAddress& naNodePublic, const std::string& strComment);
void nodeAddDomain(const std::string& strDomain, validatorSource vsWhy, std::string strComment="");
void nodeRemove(NewcoinAddress naNodePublic);
void nodeDefault(const std::string& strValidators);
void nodeAddPublic(const NewcoinAddress& naNodePublic, validatorSource vsWhy, const std::string& strComment);
void nodeAddDomain(std::string strDomain, validatorSource vsWhy, const std::string& strComment="");
void nodeRemovePublic(const NewcoinAddress& naNodePublic);
void nodeRemoveDomain(std::string strDomain);
void nodeReset();
void nodeScore();
bool nodeInUNL(const NewcoinAddress& naNodePublic);
void nodeBootstrap();
bool nodeLoad();
void nodeNetwork();
Json::Value getUnlJson();
};

View File

@@ -53,13 +53,8 @@ bool Wallet::nodeIdentityLoad()
mNodePublicKey.setNodePublic(strPublicKey);
mNodePrivateKey.setNodePrivate(strPrivateKey);
std::string strDh512, strDh1024;
db->getStr("Dh512", strDh512);
db->getStr("Dh1024", strDh1024);
mDh512 = DH_der_load_hex(strDh512);
mDh1024 = DH_der_load_hex(strDh1024);
mDh512 = DH_der_load(db->getStrBinary("Dh512"));
mDh1024 = DH_der_load(db->getStrBinary("Dh1024"));
db->endIterRows();
bSuccess = true;
@@ -81,13 +76,11 @@ bool Wallet::nodeIdentityCreate() {
// Make new key.
std::string strDh512, strDh1024;
DH_der_gen_hex(strDh512, 512); // Using hex as db->escape in insufficient.
std::string strDh512 = DH_der_gen(512);
#if 1
strDh1024 = strDh512; // For testing and most cases 512 is fine.
std::string strDh1024 = strDh512; // For testing and most cases 512 is fine.
#else
DH_der_gen_hex(strDh1024, 1024);
std::string strDh1024 = DH_der_gen(1024);
#endif
//
@@ -96,11 +89,11 @@ bool Wallet::nodeIdentityCreate() {
Database* db = theApp->getWalletDB()->getDB();
ScopedLock sl(theApp->getWalletDB()->getDBLock());
db->executeSQL(str(boost::format("INSERT INTO NodeIdentity (PublicKey,PrivateKey,Dh512,Dh1024) VALUES (%s,%s,%s,%s);")
% db->escape(naNodePublic.humanNodePublic())
% db->escape(naNodePrivate.humanNodePrivate())
% db->escape(strDh512)
% db->escape(strDh1024)));
db->executeSQL(str(boost::format("INSERT INTO NodeIdentity (PublicKey,PrivateKey,Dh512,Dh1024) VALUES ('%s','%s',%s,%s);")
% naNodePublic.humanNodePublic()
% naNodePrivate.humanNodePrivate()
% sqlEscape(strDh512)
% sqlEscape(strDh1024)));
// XXX Check error result.
std::cerr << "NodeIdentity: Created." << std::endl;

View File

@@ -59,8 +59,10 @@ void printHelp(const po::options_description& desc)
cout << " transit_set <seed> <paying_account> <transit_rate> <starts> <expires>" << endl;
cout << " tx <id>" << endl;
cout << " unl_add <domain>|<public> [<comment>]" << endl;
cout << " unl_delete <public_key>" << endl;
cout << " unl_delete <domain>|<public_key>" << endl;
cout << " unl_list" << endl;
cout << " unl_load" << endl;
cout << " unl_network" << endl;
cout << " unl_reset" << endl;
cout << " validation_create [<seed>|<pass_phrase>|<key>]" << endl;
cout << " validation_seed [<seed>|<pass_phrase>|<key>]" << endl;

View File

@@ -95,10 +95,11 @@ std::string strCopy(const std::vector<unsigned char>& vucSrc)
// DH support
//
void DH_der_gen(std::string& strDer, int iKeyLength)
std::string DH_der_gen(int iKeyLength)
{
DH* dh = 0;
int iCodes;
DH* dh = 0;
int iCodes;
std::string strDer;
do {
dh = DH_generate_parameters(iKeyLength, DH_GENERATOR_5, NULL, NULL);
@@ -111,15 +112,8 @@ void DH_der_gen(std::string& strDer, int iKeyLength)
unsigned char* next = reinterpret_cast<unsigned char *>(&strDer[0]);
(void) i2d_DHparams(dh, &next);
}
void DH_der_gen_hex(std::string& strDer, int iKeyLength)
{
std::string strBuf;
DH_der_gen(strBuf, iKeyLength);
strDer = strHex(strBuf);
return strDer;
}
DH* DH_der_load(const std::string& strDer)
@@ -129,15 +123,6 @@ DH* DH_der_load(const std::string& strDer)
return d2i_DHparams(NULL, &pbuf, strDer.size());
}
DH* DH_der_load_hex(const std::string& strDer)
{
std::string strBuf;
strUnHex(strBuf, strDer);
return DH_der_load(strBuf);
}
/*
void intIPtoStr(int ip,std::string& retStr)
{

View File

@@ -82,6 +82,11 @@ inline std::string strHex(const uint64 uiHost)
return strHex((unsigned char*) &uBig, sizeof(uBig));
}
inline static std::string sqlEscape(const std::string& strSrc)
{
return str(boost::format("X'%s'") % strHex(strSrc));
}
template<class Iterator>
bool isZero(Iterator first, int iSize)
{
@@ -100,9 +105,7 @@ std::vector<unsigned char> strCopy(const std::string& strSrc);
std::string strCopy(const std::vector<unsigned char>& vucSrc);
DH* DH_der_load(const std::string& strDer);
DH* DH_der_load_hex(const std::string& strDer);
void DH_der_gen(std::string& strDer, int iKeyLength);
void DH_der_gen_hex(std::string& strDer, int iKeyLength);
std::string DH_der_gen(int iKeyLength);
inline std::string strGetEnv(const std::string& strKey)
{

View File

@@ -9,8 +9,14 @@
# All other lines should be hankos or domain names.
#
# [validators]:
# To acquire a UNL, newcoind will probe for https web servers at the domains
# listed below in the following order: newcoin.DOMAIN, www.DOMAIN, DOMAIN
# List of nodes to accept as validators speficied by public key or domain.
#
# For domains, newcoind will probe for https web servers at the specied
# domain in the following order: newcoin.DOMAIN, www.DOMAIN, DOMAIN
#
# Examples: redstem.com
# n9KorY8QtTdRx7TVDpwnG9NvyxsDwHUKUEeDLY3AkiGncVaSXZi5
# n9MqiExBcoG19UXwoLjBJnhsxEhAZMuWwJDRdkyDz1EkEkwzQTNt John Doe
#
[validators]