mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
@@ -40,5 +40,6 @@ public:
|
||||
uint64 getBigInt(int colIndex);
|
||||
|
||||
void escape(const unsigned char* start,int size,std::string& retStr);
|
||||
|
||||
};
|
||||
|
||||
// vim:ts=4
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
73
newcoind.cfg
73
newcoind.cfg
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
34
src/Config.h
34
src/Config.h
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, },
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) + ";");
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user