diff --git a/deploy/cointoss.nsi b/deploy/cointoss.nsi new file mode 100644 index 0000000000..9e32d62bbd --- /dev/null +++ b/deploy/cointoss.nsi @@ -0,0 +1,37 @@ +Name "CoinToss" + +; The file to write +OutFile "toss install.exe" + +; The default installation directory +InstallDir "$PROGRAMFILES\CoinToss" + +; Request application privileges for Windows Vista +RequestExecutionLevel user + +;-------------------------------- + +; Pages + +Page directory +Page instfiles + +;-------------------------------- + +; The stuff to install +Section "" ;No components page, name is not important + + ; Set output path to the installation directory. + SetOutPath $INSTDIR + + ; Put file there + File ..\Release\newcoin.exe + File ..\*.dll + File "start CoinToss.bat" + File newcoind.cfg + File validators.txt + File /r /x .git ..\..\nc-client\*.* + + + +SectionEnd ; end the section diff --git a/deploy/newcoind.cfg b/deploy/newcoind.cfg new file mode 100644 index 0000000000..a4e5618295 --- /dev/null +++ b/deploy/newcoind.cfg @@ -0,0 +1,147 @@ +# +# Sample newcoind.cfg +# +# 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. +# +# --conf=: +# You may specify the location of this file with --conf=. The config +# directory is the directory containing this file. The data directory is a +# the subdirectory named "dbs". +# +# 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 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 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 +# +# [debug_logfile] +# Specifies were a debug logfile is kept. By default, no debug log is kept +# +# Example: debug.log +# +# [validators_site]: +# Specifies where to find validators.txt for UNL boostrapping and RPC command unl_network. +# During alpha testing, this defaults to: redstem.com +# +# Example: newcoin.org +# +# [unl_default]: +# XXX This should be called: [validators_file] +# 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 +# +# [validators]: +# Only valid in "newcoind.cfg", "newcoin.txt", and the referered [validators_url]. +# 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 +# +# [ips]: +# Only valid in "newcoind.cfg", "newcoin.txt", and the referered [ips_url]. +# List of ips where the Newcoin protocol is avialable. +# One ipv4 or ipv6 address per line. +# A port may optionally be specified after adding a space to the address. +# By convention, if known, IPs are listed in from most to least trusted. +# +# Examples: +# 192.168.0.1 +# 192.168.0.1 3939 +# 2001:0db8:0100:f101:0210:a4ff:fee3:9566 +# +# [peer_ip]: +# 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 allow external connections from peers. +# +# [rpc_ip]: +# 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. +# +# [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 + +[peer_port] +51235 + +[rpc_ip] +127.0.0.1 + +[rpc_port] +5005 + +[rpc_allow_remote] +1 + +[debug_logfile] +debug.log + +[validation_seed] +shh1D4oj5czH3PUEjYES8c7Bay3tE + +[unl_default] +validators.txt diff --git a/deploy/start CoinToss.bat b/deploy/start CoinToss.bat new file mode 100644 index 0000000000..b23c09ade2 --- /dev/null +++ b/deploy/start CoinToss.bat @@ -0,0 +1,3 @@ +start newcoin +sleep 4 +start index.html \ No newline at end of file diff --git a/deploy/validators.txt b/deploy/validators.txt new file mode 100644 index 0000000000..728f72bacc --- /dev/null +++ b/deploy/validators.txt @@ -0,0 +1,25 @@ +# +# Default validators.txt +# +# A list of domains to bootstrap a nodes UNLs or for clients to indirectly +# locate IPs to contact the Newcoin network. +# +# This file is UTF-8 with Dos, UNIX, or Mac style end of lines. +# Blank lines and lines starting with a '#' are ignored. +# All other lines should be hankos or domain names. +# +# [validators]: +# 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] +n9LQC4xFSWXNv1SU1sKtjrW6TZpBZSwp1nRWej8saGs155x42YFZ first +n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second +n9KXAZxiHkWuVGxDEE8boW7WmcycpZNmWei4vxVaywLZ391Nbuqx third diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 975e16da8e..ea76caa586 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -72,7 +72,7 @@ MaxSpeed true true - BOOST_TEST_NO_MAIN;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + BOOST_TEST_ALTERNATIVE_INIT_API;BOOST_TEST_NO_MAIN;_CRT_SECURE_NO_WARNINGS;_WIN32_WINNT=0x0501;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ..\OpenSSL\include;..\boost_1_47_0;..\protobuf-2.4.1\src diff --git a/src/Amount.cpp b/src/Amount.cpp index cd4cd3bc5a..88d40b912a 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -100,7 +100,7 @@ bool STAmount::setValue(const std::string& sAmount, const std::string& sCurrency if (bInteger) { - uValue = sAmount.empty() ? 0 : boost::lexical_cast(sAmount); + uValue = sAmount.empty() ? 0 : boost::lexical_cast(sAmount); iOffset = 0; } else diff --git a/src/Application.cpp b/src/Application.cpp index 4300f0bf4b..05ea8e446d 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -1,6 +1,9 @@ #include +#include +#include + #include "../database/SqliteDatabase.h" #include "Application.h" @@ -52,6 +55,11 @@ void Application::stop() std::cerr << "Stopped: " << mIOService.stopped() << std::endl; } +static void InitDB(DatabaseCon** dbCon, const char *fileName, const char *dbInit[], int dbCount) +{ + *dbCon = new DatabaseCon(fileName, dbInit, dbCount); +} + void Application::run() { assert(mTxnDB == NULL); @@ -61,11 +69,12 @@ 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); + boost::thread t1(boost::bind(&InitDB, &mTxnDB, "transaction.db", TxnDBInit, TxnDBCount)); + boost::thread t2(boost::bind(&InitDB, &mLedgerDB, "ledger.db", LedgerDBInit, LedgerDBCount)); + boost::thread t3(boost::bind(&InitDB, &mWalletDB, "wallet.db", WalletDBInit, WalletDBCount)); + boost::thread t4(boost::bind(&InitDB, &mHashNodeDB, "hashnode.db", HashNodeDBInit, HashNodeDBCount)); + boost::thread t5(boost::bind(&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount)); + t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); // // Begin validation and ip maintenance. diff --git a/src/DBInit.cpp b/src/DBInit.cpp index 51df2458ca..bc1b71217b 100644 --- a/src/DBInit.cpp +++ b/src/DBInit.cpp @@ -2,6 +2,8 @@ // Transaction database holds transactions and public keys const char *TxnDBInit[] = { + "BEGIN TRANSACTION;", + "CREATE TABLE Transactions ( \ TransID CHARACTER(64) PRIMARY KEY, \ TransType CHARACTER(24), \ @@ -21,13 +23,17 @@ const char *TxnDBInit[] = { LedgerSeq BIGINT UNSIGNED \ );", "CREATE INDEX AcctTxindex ON \ - AccountTransactions(Account, LedgerSeq, TransID);" + AccountTransactions(Account, LedgerSeq, TransID);", + + "END TRANSACTION;" }; int TxnDBCount = sizeof(TxnDBInit) / sizeof(const char *); // Ledger database holds ledgers and ledger confirmations const char *LedgerDBInit[] = { + "BEGIN TRANSACTION;", + "CREATE TABLE Ledgers ( \ LedgerHash CHARACTER(64) PRIMARY KEY, \ LedgerSeq BIGINT UNSIGNED, \ @@ -46,8 +52,9 @@ const char *LedgerDBInit[] = { Signature BLOB \ );", "CREATE INDEX LedgerConfByHash ON \ - LedgerConfirmations(LedgerHash)" + LedgerConfirmations(LedgerHash)", #endif + "END TRANSACTION;" }; int LedgerDBCount = sizeof(LedgerDBInit) / sizeof(const char *); @@ -55,6 +62,8 @@ int LedgerDBCount = sizeof(LedgerDBInit) / sizeof(const char *); // Wallet database holds local accounts and trusted nodes const char *WalletDBInit[] = { // Node identity must be persisted for CAS routing and responsibilities. + "BEGIN TRANSACTION;", + "CREATE TABLE NodeIdentity ( \ PublicKey CHARACTER(53), \ PrivateKey CHARACTER(52), \ @@ -221,13 +230,17 @@ const char *WalletDBInit[] = { );", "CREATE INDEX PeerScanIndex ON \ - PeerIps(ScanNext);" + PeerIps(ScanNext);", + + "END TRANSACTION;" }; int WalletDBCount = sizeof(WalletDBInit) / sizeof(const char *); // Hash node database holds nodes indexed by hash const char *HashNodeDBInit[] = { + "BEGIN TRANSACTION;", + "CREATE TABLE CommittedObjects ( \ Hash CHARACTER(64) PRIMARY KEY, \ ObjType CHAR(1) NOT NULL, \ @@ -236,7 +249,9 @@ const char *HashNodeDBInit[] = { );", "CREATE INDEX ObjectLocate ON \ - CommittedObjects(LedgerIndex, ObjType);" + CommittedObjects(LedgerIndex, ObjType);", + + "END TRANSACTION;" }; int HashNodeDBCount = sizeof(HashNodeDBInit) / sizeof(const char *); diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index a9edbf0c38..fc6a798c0e 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -16,6 +16,8 @@ // #define LC_DEBUG +// TODO: If we don't have the previousLCL, check if we got it. If so, change modes + TransactionAcquire::TransactionAcquire(const uint256& hash) : PeerSet(hash, 1), mFilter(&theApp->getNodeCache()), mHaveRoot(false) { @@ -188,12 +190,15 @@ int LCTransaction::getAgreeLevel() return (mNays * 100 + 100) / (mYays + mNays + 1); } -LedgerConsensus::LedgerConsensus(Ledger::pointer previousLedger, uint32 closeTime) - : mState(lcsPRE_CLOSE), mCloseTime(closeTime), mPreviousLedger(previousLedger) +LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::pointer previousLedger, uint32 closeTime) + : mState(lcsPRE_CLOSE), mCloseTime(closeTime), mPrevLedgerHash(prevLCLHash), mPreviousLedger(previousLedger) { + mValSeed = theConfig.VALIDATION_SEED; Log(lsDEBUG) << "Creating consensus object"; Log(lsTRACE) << "LCL:" << previousLedger->getHash().GetHex() <<", ct=" << closeTime; - if (theConfig.VALIDATION_SEED.isValid()) + if (previousLedger->getHash() != prevLCLHash) + mHaveCorrectLCL = mProposing = mValidating = false; + else if (mValSeed.isValid()) { mValidating = true; mProposing = theApp->getOPs().getOperatingMode() == NetworkOPs::omFULL; @@ -205,7 +210,7 @@ void LedgerConsensus::takeInitialPosition(Ledger::pointer initialLedger) { SHAMap::pointer initialSet = initialLedger->peekTransactionMap()->snapShot(false); uint256 txSet = initialSet->getHash(); - assert (initialLedger->getParentHash() == mPreviousLedger->getHash()); + assert (!mHaveCorrectLCL || (initialLedger->getParentHash() == mPreviousLedger->getHash())); // if any peers have taken a contrary position, process disputes boost::unordered_set found; @@ -221,9 +226,9 @@ void LedgerConsensus::takeInitialPosition(Ledger::pointer initialLedger) } } - if (mProposing) + if (mValidating) mOurPosition = boost::make_shared - (theConfig.VALIDATION_SEED, initialLedger->getParentHash(), txSet); + (mValSeed, initialLedger->getParentHash(), txSet); else mOurPosition = boost::make_shared(initialLedger->getParentHash(), txSet); mapComplete(txSet, initialSet, false); @@ -317,13 +322,21 @@ void LedgerConsensus::adjustCount(SHAMap::pointer map, const std::vectorgetLedgerSeq()); - s.set_networktime(theApp->getOPs().getNetworkTimeNC()); - uint256 plhash = ledger->getParentHash(); - s.set_previousledgerhash(plhash.begin(), plhash.size()); + if (!mHaveCorrectLCL) + s.set_newevent(newcoin::neLOST_SYNC); + else + { + s.set_newevent(event); + s.set_ledgerseq(ledger->getLedgerSeq()); + s.set_networktime(theApp->getOPs().getNetworkTimeNC()); + uint256 hash = ledger->getParentHash(); + s.set_previousledgerhash(hash.begin(), hash.size()); + hash = ledger->getHash(); + s.set_ledgerhash(hash.begin(), hash.size()); + } PackedMessage::pointer packet = boost::make_shared(s, newcoin::mtSTATUS_CHANGE); theApp->getConnectionPool().relayMessage(NULL, packet); + Log(lsINFO) << "send status change to peer"; } void LedgerConsensus::abort() @@ -724,7 +737,7 @@ void LedgerConsensus::accept(SHAMap::pointer set) assert(set->getHash() == mOurPosition->getCurrentHash()); Log(lsINFO) << "Computing new LCL based on network consensus"; Log(lsDEBUG) << "Consensus " << mOurPosition->getCurrentHash().GetHex(); - Log(lsDEBUG) << "Previous LCL " << mPreviousLedger->getHash().GetHex(); + Log(lsDEBUG) << "Previous LCL " << mPrevLedgerHash.GetHex(); Ledger::pointer newLCL = boost::make_shared(false, boost::ref(*mPreviousLedger)); @@ -747,15 +760,6 @@ void LedgerConsensus::accept(SHAMap::pointer set) uint256 newLCLHash = newLCL->getHash(); Log(lsTRACE) << "newLCL " << newLCLHash.GetHex(); -#ifdef DEBUG - if (1) - { - Log(lsTRACE) << "newLCL after transactions"; - Json::Value p; - newLCL->addJson(p, LEDGER_JSON_DUMP_TXNS | LEDGER_JSON_DUMP_STATE); - ssw.write(Log(lsTRACE).ref(), p); - } -#endif Ledger::pointer newOL = boost::make_shared(true, boost::ref(*newLCL)); @@ -811,13 +815,17 @@ void LedgerConsensus::accept(SHAMap::pointer set) Json::Value p; newOL->addJson(p, LEDGER_JSON_DUMP_TXNS | LEDGER_JSON_DUMP_STATE); ssw.write(Log(lsTRACE).ref(), p); + Log(lsINFO) << "newLCL after transactions"; + Json::Value p2; + newLCL->addJson(p2, LEDGER_JSON_DUMP_TXNS | LEDGER_JSON_DUMP_STATE); + ssw.write(Log(lsTRACE).ref(), p2); } #endif if (mValidating) { SerializedValidation::pointer v = boost::make_shared - (newLCLHash, mOurPosition->peekSeed(), true); + (newLCLHash, newLCL->getCloseTimeNC(), mOurPosition->peekSeed(), true); v->setTrusted(); // FIXME: If not proposing, set not full theApp->getValidations().addValidation(v); @@ -825,9 +833,10 @@ void LedgerConsensus::accept(SHAMap::pointer set) newcoin::TMValidation val; val.set_validation(&validation[0], validation.size()); theApp->getConnectionPool().relayMessage(NULL, boost::make_shared(val, newcoin::mtVALIDATION)); - Log(lsINFO) << "Validation sent " << newLCL->getHash().GetHex(); + Log(lsINFO) << "Validation sent " << newLCLHash.GetHex(); } - statusChange(newcoin::neACCEPTED_LEDGER, newOL); + else Log(lsWARNING) << "Not validating"; + statusChange(newcoin::neACCEPTED_LEDGER, newLCL); } void LedgerConsensus::endConsensus() diff --git a/src/LedgerConsensus.h b/src/LedgerConsensus.h index 349722951d..fa8be9ceb3 100644 --- a/src/LedgerConsensus.h +++ b/src/LedgerConsensus.h @@ -83,9 +83,11 @@ class LedgerConsensus : public boost::enable_shared_from_this protected: LCState mState; uint32 mCloseTime; + uint256 mPrevLedgerHash; Ledger::pointer mPreviousLedger; LedgerProposal::pointer mOurPosition; - bool mProposing, mValidating; + NewcoinAddress mValSeed; + bool mProposing, mValidating, mHaveCorrectLCL; // Convergence tracking, trusted peers indexed by hash of public key boost::unordered_map mPeerPositions; @@ -130,7 +132,7 @@ protected: void endConsensus(); public: - LedgerConsensus(Ledger::pointer previousLedger, uint32 closeTime); + LedgerConsensus(const uint256& prevLCLHash, Ledger::pointer previousLedger, uint32 closeTime); int startup(); diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index 29166e1b4e..bad18d8862 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -251,7 +251,7 @@ void NetworkOPs::setStateTimer(int sec) uint64 consensusTime = mLedgerMaster->getCurrentLedger()->getCloseTimeNC() - LEDGER_WOBBLE_TIME; uint64 now = getNetworkTimeNC(); - if (now >= consensusTime) sec = 0; + if (now >= consensusTime) sec = 1; else if (sec > (consensusTime - now)) sec = (consensusTime - now); } mNetTimer.expires_from_now(boost::posix_time::seconds(sec)); @@ -269,8 +269,6 @@ public: { if (trustedValidations > v.trustedValidations) return true; if (trustedValidations < v.trustedValidations) return false; - if (untrustedValidations > v.untrustedValidations) return true; - if (untrustedValidations < v.untrustedValidations) return false; if (nodesUsing > v.nodesUsing) return true; if (nodesUsing < v.nodesUsing) return false; return highNode > v.highNode; @@ -308,7 +306,6 @@ void NetworkOPs::checkState(const boost::system::error_code& result) return; } - // FIXME: Don't check unless last closed ledger is at least some seconds old // If full or tracking, check only at wobble time! if (checkLastClosedLedger(peerList)) @@ -334,14 +331,11 @@ void NetworkOPs::checkState(const boost::system::error_code& result) // check if the ledger is good enough to go to omFULL // Note: Do not go to omFULL if we don't have the previous ledger // check if the ledger is bad enough to go to omCONNECTED -- TODO - if (theConfig.VALIDATION_SEED.isValid()) - { - if (theApp->getOPs().getNetworkTimeNC() < - (theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC() + 4)) - setMode(omFULL); - else - Log(lsWARNING) << "Too late to go to full, try next ledger"; - } + if (theApp->getOPs().getNetworkTimeNC() < + (theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC() + 4)) + setMode(omFULL); + else + Log(lsWARNING) << "Too late to go to full, will try in consensus window"; } if (mMode == omFULL) @@ -363,8 +357,11 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis // Do we have sufficient validations for our last closed ledger? Or do sufficient nodes // agree? And do we have no better ledger available? // If so, we are either tracking or full. - boost::unordered_map ledgers; + // FIXME: We may have a ledger with many recent validations but that no directly-connected + // node is using. THis is kind of fundamental. + + boost::unordered_map ledgers; for (std::vector::const_iterator it = peerList.begin(), end = peerList.end(); it != end; ++it) { if (!*it) @@ -377,11 +374,10 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis if (!!peerLedger) { // FIXME: If we have this ledger, don't count it if it's too far past its close time - bool isNew = ledgers.find(peerLedger) == ledgers.end(); ValidationCount& vc = ledgers[peerLedger]; - if (isNew) + if (vc.nodesUsing == 0) { - theApp->getValidations().getValidationCount(peerLedger, + theApp->getValidations().getValidationCount(peerLedger, true, vc.trustedValidations, vc.untrustedValidations); Log(lsTRACE) << peerLedger.GetHex() << " has " << vc.trustedValidations << " trusted validations and " << vc.untrustedValidations << " untrusted"; @@ -395,19 +391,27 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis Ledger::pointer currentClosed = mLedgerMaster->getClosedLedger(); uint256 closedLedger = currentClosed->getHash(); - ValidationCount& vc = ledgers[closedLedger]; - if ((vc.nodesUsing == 0) || (theApp->getWallet().getNodePublic() > vc.highNode)) - vc.highNode = theApp->getWallet().getNodePublic(); - ++ledgers[closedLedger].nodesUsing; + ValidationCount& ourVC = ledgers[closedLedger]; + if (ourVC.nodesUsing == 0) + { + ourVC.highNode = theApp->getWallet().getNodePublic(); + theApp->getValidations().getValidationCount(closedLedger, true, + ourVC.trustedValidations, ourVC.untrustedValidations); + } + ++ourVC.nodesUsing; + ValidationCount bestVC = ourVC; // 3) Is there a network ledger we'd like to switch to? If so, do we have it? bool switchLedgers = false; for(boost::unordered_map::iterator it = ledgers.begin(), end = ledgers.end(); it != end; ++it) { - if (it->second > vc) + Log(lsTRACE) << "L: " << it->first.GetHex() << + " t=" << it->second.trustedValidations << ", u=" << it->second.untrustedValidations << + ", n=" << it->second.nodesUsing; + if (it->second > bestVC) { - vc = it->second; + bestVC = it->second; closedLedger = it->first; switchLedgers = true; } @@ -467,8 +471,10 @@ void NetworkOPs::switchLastClosedLedger(Ledger::pointer newLedger) s.set_newevent(newcoin::neSWITCHED_LEDGER); s.set_ledgerseq(newLedger->getLedgerSeq()); s.set_networktime(theApp->getOPs().getNetworkTimeNC()); - uint256 plhash = newLedger->getParentHash(); - s.set_previousledgerhash(plhash.begin(), plhash.size()); + uint256 hash = newLedger->getParentHash(); + s.set_previousledgerhash(hash.begin(), hash.size()); + hash = newLedger->getHash(); + s.set_ledgerhash(hash.begin(), hash.size()); PackedMessage::pointer packet = boost::make_shared(s, newcoin::mtSTATUS_CHANGE); theApp->getConnectionPool().relayMessage(NULL, packet); } @@ -491,8 +497,9 @@ int NetworkOPs::beginConsensus(Ledger::pointer closingLedger) // Create a consensus object to get consensus on this ledger if (!!mConsensus) mConsensus->abort(); prevLedger->setImmutable(); - mConsensus = boost::make_shared - (prevLedger, theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC()); + mConsensus = boost::make_shared( + prevLedger->getHash(), // FIXME: Only do this if the previous ledger is the consensus previous ledger + prevLedger, theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC()); Log(lsDEBUG) << "Pre-close time, initiating consensus engine"; return mConsensus->startup(); @@ -568,6 +575,12 @@ void NetworkOPs::mapComplete(const uint256& hash, SHAMap::pointer map) void NetworkOPs::endConsensus() { + uint256 deadLedger = theApp->getMasterLedger().getClosedLedger()->getParentHash(); + Log(lsTRACE) << "Ledger " << deadLedger.GetHex() << " is now dead"; + std::vector peerList = theApp->getConnectionPool().getPeerVector(); + for (std::vector::const_iterator it = peerList.begin(), end = peerList.end(); it != end; ++it) + if (*it && ((*it)->getClosedLedgerHash() == deadLedger)) + (*it)->cycleStatus(); mConsensus = boost::shared_ptr(); } diff --git a/src/Peer.cpp b/src/Peer.cpp index 9839ed2f6a..fd18cf6090 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -823,21 +823,23 @@ void Peer::recvStatus(newcoin::TMStatusChange& packet) packet.set_networktime(theApp->getOPs().getNetworkTimeNC()); mLastStatus = packet; + if (packet.newevent() == newcoin::neLOST_SYNC) + { + mPreviousLedgerHash.zero(); + mClosedLedgerHash.zero(); + } if (packet.has_ledgerhash() && (packet.ledgerhash().size() == (256 / 8))) { // a peer has changed ledgers - if (packet.has_previousledgerhash() && (packet.previousledgerhash().size() == (256 / 8))) - memcpy(mPreviousLedgerHash.begin(), packet.previousledgerhash().data(), 256 / 8); - else - mPreviousLedgerHash = mClosedLedgerHash; memcpy(mClosedLedgerHash.begin(), packet.ledgerhash().data(), 256 / 8); mClosedLedgerTime = ptFromSeconds(packet.networktime()); Log(lsTRACE) << "peer LCL is " << mClosedLedgerHash.GetHex(); } - else if(packet.has_previousledgerhash() && packet.previousledgerhash().size() == (256 / 8)) + else mClosedLedgerHash.zero(); + if (packet.has_previousledgerhash() && packet.previousledgerhash().size() == (256 / 8)) { - memcpy(mClosedLedgerHash.begin(), packet.previousledgerhash().data(), 256 / 8); - mClosedLedgerTime = ptFromSeconds(packet.networktime()); + memcpy(mPreviousLedgerHash.begin(), packet.previousledgerhash().data(), 256 / 8); } + else mPreviousLedgerHash.zero(); } void Peer::recvGetLedger(newcoin::TMGetLedger& packet) diff --git a/src/Peer.h b/src/Peer.h index e707ec28ce..6d38399ec7 100644 --- a/src/Peer.h +++ b/src/Peer.h @@ -136,6 +136,7 @@ public: uint256 getClosedLedgerHash() const { return mClosedLedgerHash; } NewcoinAddress getNodePublic() const { return mNodePublic; } + void cycleStatus() { mPreviousLedgerHash = mClosedLedgerHash; mClosedLedgerHash.zero(); } }; #endif diff --git a/src/SerializedObject.h b/src/SerializedObject.h index 3c322486da..ab115e1254 100644 --- a/src/SerializedObject.h +++ b/src/SerializedObject.h @@ -38,6 +38,7 @@ enum SOE_Field sfBorrowRate, sfBorrowStart, sfBorrower, + sfCloseTime, sfCurrency, sfCurrencyIn, sfCurrencyOut, diff --git a/src/SerializedValidation.cpp b/src/SerializedValidation.cpp index 7647be4833..9c19bc7615 100644 --- a/src/SerializedValidation.cpp +++ b/src/SerializedValidation.cpp @@ -4,6 +4,7 @@ SOElement SerializedValidation::sValidationFormat[] = { { sfFlags, "Flags", STI_UINT32, SOE_FLAGS, 0 }, { sfLedgerHash, "LedgerHash", STI_HASH256, SOE_REQUIRED, 0 }, + { sfCloseTime, "CloseTime", STI_UINT64, SOE_REQUIRED, 0 }, { sfSigningKey, "SigningKey", STI_VL, SOE_REQUIRED, 0 }, { sfExtensions, "Extensions", STI_TL, SOE_IFFLAG, 0x01000000 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 }, @@ -18,11 +19,14 @@ SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSi if (!isValid()) throw std::runtime_error("Invalid validation"); } -SerializedValidation::SerializedValidation(const uint256& ledgerHash, const NewcoinAddress& naSeed, bool isFull) +SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint64 closeTime, + const NewcoinAddress& naSeed, bool isFull) : STObject(sValidationFormat), mSignature("Signature"), mTrusted(false) { setValueFieldH256(sfLedgerHash, ledgerHash); - setValueFieldVL(sfSigningKey, NewcoinAddress::createNodePublic(naSeed).getNodePublic()); + setValueFieldU64(sfCloseTime, closeTime); + if (naSeed.isValid()) + setValueFieldVL(sfSigningKey, NewcoinAddress::createNodePublic(naSeed).getNodePublic()); if (!isFull) setFlag(sFullFlag); NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), mSignature.peekValue()); @@ -46,6 +50,11 @@ uint256 SerializedValidation::getLedgerHash() const return getValueFieldH256(sfLedgerHash); } +uint64 SerializedValidation::getCloseTime() const +{ + return getValueFieldU64(sfCloseTime); +} + bool SerializedValidation::isValid() const { try diff --git a/src/SerializedValidation.h b/src/SerializedValidation.h index 987a883234..ab0886a6bf 100644 --- a/src/SerializedValidation.h +++ b/src/SerializedValidation.h @@ -23,9 +23,10 @@ public: SerializedValidation(SerializerIterator& sit, bool checkSignature = true); SerializedValidation(const Serializer& s, bool checkSignature = true); - SerializedValidation(const uint256& ledgerHash, const NewcoinAddress& naSeed, bool isFull); + SerializedValidation(const uint256& ledgerHash, uint64 closeTime, const NewcoinAddress& naSeed, bool isFull); uint256 getLedgerHash() const; + uint64 getCloseTime() const; NewcoinAddress getSignerPublic() const; bool isValid() const; bool isFull() const; @@ -33,7 +34,7 @@ public: CKey::pointer getSigningKey() const; uint256 getSigningHash() const; - void setTrusted() { mTrusted = true; } + void setTrusted() { mTrusted = true; } void addSigned(Serializer&) const; void addSignature(Serializer&) const; std::vector getSigned() const; diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index 7da2747365..b30ea4a00f 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -2,22 +2,40 @@ #include "ValidationCollection.h" #include "Application.h" +#include "LedgerTiming.h" #include "Log.h" bool ValidationCollection::addValidation(SerializedValidation::pointer val) { - if(theApp->getUNL().nodeInUNL(val->getSignerPublic())) - val->setTrusted(); + bool isTrusted = false; + if (theApp->getUNL().nodeInUNL(val->getSignerPublic())) + { + uint64 now = theApp->getOPs().getNetworkTimeNC(); + uint64 valClose = val->getCloseTime(); + if ((now > valClose) && (now < (valClose + 2 * LEDGER_INTERVAL))) + isTrusted = true; + else + Log(lsWARNING) << "Received stale validation"; + } uint256 hash = val->getLedgerHash(); uint160 node = val->getSignerPublic().getNodeID(); - boost::mutex::scoped_lock sl(mValidationLock); - bool ret = mValidations[hash].insert(std::make_pair(node, val)).second; - if (ret) - Log(lsINFO) << "Val for " << hash.GetHex() << " from " << node.GetHex() << " added " << - (val->isTrusted() ? "trusted" : "UNtrusted"); - return ret; + { + boost::mutex::scoped_lock sl(mValidationLock); + if (!mValidations[hash].insert(std::make_pair(node, val)).second) + return false; + if (isTrusted) + { + boost::unordered_map::iterator it = mCurrentValidations.find(node); + if ((it == mCurrentValidations.end()) || (val->getCloseTime() >= it->second->getCloseTime())) + mCurrentValidations[node] = val; + } + } + + Log(lsINFO) << "Val for " << hash.GetHex() << " from " << node.GetHex() << " added " << + (val->isTrusted() ? "trusted" : "UNtrusted"); + return true; } ValidationSet ValidationCollection::getValidations(const uint256& ledger) @@ -31,17 +49,24 @@ ValidationSet ValidationCollection::getValidations(const uint256& ledger) return ret; } -void ValidationCollection::getValidationCount(const uint256& ledger, int& trusted, int &untrusted) +void ValidationCollection::getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int &untrusted) { trusted = untrusted = 0; boost::mutex::scoped_lock sl(mValidationLock); boost::unordered_map::iterator it = mValidations.find(ledger); + uint64 now = theApp->getOPs().getNetworkTimeNC(); if (it != mValidations.end()) { - for (ValidationSet::iterator vit = it->second.begin(), end = it->second.end(); - vit != end; ++vit) + for (ValidationSet::iterator vit = it->second.begin(), end = it->second.end(); vit != end; ++vit) { - if(vit->second->isTrusted()) + bool trusted = vit->second->isTrusted(); + if (trusted && currentOnly) + { + uint64 closeTime = vit->second->getCloseTime(); + if ((now < closeTime) || (now > (closeTime + 2 * LEDGER_INTERVAL))) + trusted = false; + } + if (trusted) ++trusted; else ++untrusted; diff --git a/src/ValidationCollection.h b/src/ValidationCollection.h index 3f41e0fc0f..62540f5853 100644 --- a/src/ValidationCollection.h +++ b/src/ValidationCollection.h @@ -16,13 +16,14 @@ class ValidationCollection boost::mutex mValidationLock; boost::unordered_map mValidations; + boost::unordered_map mCurrentValidations; public: ValidationCollection() { ; } bool addValidation(SerializedValidation::pointer); ValidationSet getValidations(const uint256& ledger); - void getValidationCount(const uint256& ledger, int& trusted, int& untrusted); + void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted); }; #endif diff --git a/src/newcoin.proto b/src/newcoin.proto index 38be08e20c..1b4e30f556 100644 --- a/src/newcoin.proto +++ b/src/newcoin.proto @@ -88,6 +88,7 @@ enum NodeEvent { neCLOSING_LEDGER = 1; // closing a ledger because its close time has come neACCEPTED_LEDGER = 2; // accepting a closed ledger, we have finished computing it neSWITCHED_LEDGER = 3; // changing due to network consensus + neLOST_SYNC = 4; } message TMStatusChange {