diff --git a/denarius-qt.pro b/denarius-qt.pro index 905637ee..c61ee888 100644 --- a/denarius-qt.pro +++ b/denarius-qt.pro @@ -1,6 +1,6 @@ TEMPLATE = app TARGET = Denarius -VERSION = 3.3.3.0 +VERSION = 3.3.6.0 INCLUDEPATH += src src/json src/qt src/tor src/qt/plugins/mrichtexteditor DEFINES += QT_GUI BOOST_THREAD_USE_LIB BOOST_SPIRIT_THREADSAFE CONFIG += no_include_pwd @@ -221,6 +221,7 @@ HEADERS += src/qt/bitcoingui.h \ src/qt/intro.h \ src/qt/transactiontablemodel.h \ src/qt/addresstablemodel.h \ + src/qt/peertablemodel.h \ src/qt/optionsdialog.h \ src/qt/coincontroldialog.h \ src/qt/coincontroltreewidget.h \ @@ -342,6 +343,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \ src/qt/intro.cpp \ src/qt/transactiontablemodel.cpp \ src/qt/addresstablemodel.cpp \ + src/qt/peertablemodel.cpp \ src/qt/optionsdialog.cpp \ src/qt/sendcoinsdialog.cpp \ src/qt/coincontroldialog.cpp \ diff --git a/src/clientversion.h b/src/clientversion.h index 4a0da430..2d5cce40 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -8,7 +8,7 @@ // These need to be macros, as version.cpp's and bitcoin-qt.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 3 #define CLIENT_VERSION_MINOR 3 -#define CLIENT_VERSION_REVISION 3 +#define CLIENT_VERSION_REVISION 6 #define CLIENT_VERSION_BUILD 0 // Converts the parameter X to a string after macro replacement on X has been performed. diff --git a/src/fortuna.cpp b/src/fortuna.cpp index 42fc22d6..fe61c027 100644 --- a/src/fortuna.cpp +++ b/src/fortuna.cpp @@ -36,7 +36,6 @@ std::vector vecFortunastakesUsed; map mapFortunaBroadcastTxes; // CActiveFortunastake activeFortunastake; - // count peers we've requested the list from int RequestedFortunaStakeList = 0; @@ -1018,11 +1017,8 @@ void ThreadCheckForTunaPool(void* parg) //let's connect to a random fortunastake every minute! int fs = rand() % vecFortunastakes.size(); CService addr = vecFortunastakes[fs].addr; - if(ConnectNode((CAddress)addr, NULL, true)){ - if (fDebug) printf("successfully connected to fortunastake at %s\n",addr.ToStringIPPort().c_str()); - } else { - printf("error connecting to fortunastake at %s\n",addr.ToStringIPPort().c_str()); - } + AddOneShot(addr.ToStringIPPort()); + if (fDebug) printf("added fortunastake at %s to connection attempts\n",addr.ToStringIPPort().c_str()); //if we're low on peers, let's connect to some random ipv4 fortunastakes. ipv6 probably won't route anyway @@ -1032,13 +1028,8 @@ void ThreadCheckForTunaPool(void* parg) int fs = rand() % vecFortunastakes.size(); CService addr = vecFortunastakes[fs].addr; if (addr.IsIPv4() && !addr.IsLocal()) { - if(ConnectNode((CAddress)addr, NULL, true)){ - if (fDebug) printf("successfully connected to fortunastake at %s\n",addr.ToStringIPPort().c_str()); - } else { - printf("error connecting to fortunastake at %s\n",addr.ToStringIPPort().c_str()); - } + AddOneShot(addr.ToStringIPPort()); } - //MilliSleep(250); // 250 msecs * 50 nodes = 42.5sec, just in time to run this again if needed! } } diff --git a/src/fortunastake.cpp b/src/fortunastake.cpp index 78dd1fa6..77179e0f 100644 --- a/src/fortunastake.cpp +++ b/src/fortunastake.cpp @@ -22,6 +22,7 @@ CCriticalSection cs_fortunastakes; std::vector vecFortunastakes; std::vector > vecFortunastakeScores; std::vector vecFortunastakeScoresList; +CFortunaPayments ranks; uint256 vecFortunastakeScoresListHash; std::vector > vecFortunastakeRanks; /** Object for who's going to get paid on which blocks */ @@ -202,6 +203,9 @@ void ProcessMessageFortunastake(CNode* pfrom, std::string& strCommand, CDataStre // no need to look up the payment amounts right now, they aren't eligible for payment now anyway //int payments = mn.UpdateLastPaidAmounts(pindex, 1000, value); // do a search back 1000 blocks when receiving a new fortunastake to find their last payment, payments = number of payments received, value = amount + + //TODO: Set a timer to update ranks after a second + if (fDebugFS) printf("Registered new fortunastake %s (%i/%i)\n", addr.ToString().c_str(), count, current); vecFortunastakes.push_back(mn); @@ -432,10 +436,10 @@ struct CompareLastPay bool operator()(const pair& t1, const pair& t2) const { - if (t1.second->IsActive(pindex) == t2.second->IsActive(pindex)) { - return (t1.second->payValue == t2.second->payValue ? t1.second->CalculateScore(1, pindex->nHeight) > t2.second->CalculateScore(1, pindex->nHeight) : t1.second->payValue > t2.second->payValue); + if (t1.second->IsActive() == t2.second->IsActive()) { + return (t1.second->payValue == t2.second->payValue ? t1.second->pubkey.GetHash() > t2.second->pubkey.GetHash() : t1.second->payValue > t2.second->payValue); } else { - if (t1.second->IsActive(pindex) < t2.second->IsActive(pindex)) return true; //always put actives before non-actives + return (t1.second->IsActive() < t2.second->IsActive()); //always put actives before non-actives } return false; } @@ -529,13 +533,17 @@ int GetCurrentFortunaStake(int mod, int64_t nBlockHeight, int minProtocol) bool GetFortunastakeRanks(CBlockIndex* pindex) { - if (!pindex || IsInitialBlockDownload() || vecFortunastakes.size() == 0) return true; + LOCK(cs_fortunastakes); + int64_t nStartTime = GetTimeMillis(); + if (fDebug) printf("GetFortunastakeRanks: "); + if (!pindex || pindex == NULL || pindex->pprev == NULL || IsInitialBlockDownload() || vecFortunastakes.size() == 0) return true; - vecFortunastakeScores.clear(); int i = 0; - if (vecFortunastakeScoresListHash == pindex->GetBlockHash()) { + vecFortunastakeScores.clear(); + if (vecFortunastakeScoresListHash.size() > 0 && vecFortunastakeScoresListHash == pindex->GetBlockHash()) { // if ScoresList was calculated for the current pindex hash, then just use that list // TODO: make a vector of these somehow + if (fDebug) printf(" STARTCOPY (%"PRId64"ms)", GetTimeMillis() - nStartTime); BOOST_FOREACH(CFortunaStake& mn, vecFortunastakeScoresList) { i++; @@ -543,7 +551,13 @@ bool GetFortunastakeRanks(CBlockIndex* pindex) } } else { vecFortunastakeScoresList.clear(); + // now we've put the data in, let's recalculate the ranks. + if (GetBoolArg("-newranksystem",false)) ranks.initialize(pindex); + ranks.update(pindex,FortunaReorgBlock); // this should be set true the first time this is run + FortunaReorgBlock = false; // reset reorg flag, we can check now it's updated + // now we build the list for sorting + if (fDebug) printf(" STARTLOOP (%"PRId64"ms)", GetTimeMillis() - nStartTime); BOOST_FOREACH(CFortunaStake& mn, vecFortunastakes) { mn.Check(); @@ -555,27 +569,32 @@ bool GetFortunastakeRanks(CBlockIndex* pindex) int value = -1; // CBlockIndex* pindex = pindexBest; // don't use the best chain, use the chain we're asking about! - int payments = mn.UpdateLastPaidAmounts(pindex, max(FORTUNASTAKE_FAIR_PAYMENT_MINIMUM, (int)mnCount) * FORTUNASTAKE_FAIR_PAYMENT_ROUNDS, value); // do a search back 1000 blocks when receiving a new fortunastake to find their last payment, payments = number of payments received, value = amount + // int payments = mn.UpdateLastPaidAmounts(pindex, max(FORTUNASTAKE_FAIR_PAYMENT_MINIMUM, (int)mnCount) * FORTUNASTAKE_FAIR_PAYMENT_ROUNDS, value); // do a search back 1000 blocks when receiving a new fortunastake to find their last payment, payments = number of payments received, value = amount + vecFortunastakeScores.push_back(make_pair(value, &mn)); vecFortunastakeScoresList.push_back(mn); } + vecFortunastakeScoresListHash = pindex->GetBlockHash(); } // TODO: Store the whole Scores vector in a caching hash map, maybe need hashPrev as well to make sure it re calculates any different chains with the same end block? //vecFortunastakeScoresCache.insert(make_pair(pindex->GetBlockHash(), vecFortunastakeScoresList)); + if (fDebug) printf(" SORT (%"PRId64"ms)", GetTimeMillis() - nStartTime); sort(vecFortunastakeScores.rbegin(), vecFortunastakeScores.rend(), CompareLastPay(pindex)); // sort requires current pindex for modulus as pindexBest is different between clients + i = 0; // set ranks BOOST_FOREACH(PAIRTYPE(int, CFortunaStake*)& s, vecFortunastakeScores) { i++; + s.first = i; s.second->nRank = i; } - + if (fDebug) printf(" DONE (%"PRId64"ms)\n", GetTimeMillis() - nStartTime); return true; } @@ -592,17 +611,21 @@ int GetFortunastakeRank(CFortunaStake &tmn, CBlockIndex* pindex, int minProtocol if (s.second->vin == tmn.vin) return i; } + return 0; } bool CheckFSPayment(CBlockIndex* pindex, int64_t value, CFortunaStake &mn) { + if (mn.nBlockLastPaid == 0) return true; // if we didn't find a payment for this MN, let it through regardless of rate // find height // calculate average payment across all FS // check if value is > 25% higher nAverageFSIncome = avg2(vecFortunastakeScoresList); + if (nAverageFSIncome < 1 * COIN) return true; // if we can't calculate a decent average, then let the payment through int64_t max = nAverageFSIncome * 10 / 8; if (value > max) { return false; } + return true; } int64_t avg2(std::vector const& v) { @@ -611,6 +634,8 @@ int64_t avg2(std::vector const& v) { for (int i = 0; i < v.size(); i++) { int64_t x = v[i].payValue; int64_t delta = x - mean; + //TODO: implement in mandatory update, will reduce average & lead to rejections + //if (v[i].payValue > 2*COIN) { continue; } // don't consider payees above 2.00000000D (pos only / lucky payees) if (v[i].payValue < 1*COIN) { continue; } // don't consider payees below 1.00000000D (pos only / new payees) mean += delta/++n; } @@ -630,6 +655,7 @@ int GetFortunastakeByRank(int findRank, int64_t nBlockHeight, int minProtocol) if (i == findRank) return s.first; } + return 0; } //Get the last hash that matches the modulus given. Processed in reverse order @@ -678,6 +704,7 @@ bool CFortunaStake::GetPaymentInfo(const CBlockIndex *pindex, int64_t &totalValu actualRate = actualPayments / requiredRate; // TODO: stop payment if fortunastake vin age is under mnCount*30 old if (actualRate > 0) return true; + return false; } float CFortunaStake::GetPaymentRate(const CBlockIndex *pindex) @@ -697,15 +724,20 @@ int CFortunaStake::SetPayRate(int nHeight) scanBack += nHeight - pindexBest->nHeight; } // if going past current height, add to scan back height to account for how far it is - e.g. 200 in front will get 200 more blocks to smooth it out + // reset to default + payCount = 0; + payValue = 0; + payRate = 0; + if (payData.size()>0) { // printf("Using fortunastake cached payments data for pay rate"); // printf(" (payInfo:%d@%f)...", payCount, payRate); int64_t amount = 0; int matches = 0; - BOOST_FOREACH(PAIRTYPE(int, int64_t) &item, payData) + BOOST_FOREACH(CFortunaPayData &item, payData) { - if (item.first > nHeight - scanBack) { // find payments in last scanrange - amount += item.second; + if (item.height > nHeight - scanBack && mapBlockIndex.count(item.hash)) { // find payments in last scanrange + amount += item.amount; matches++; } } @@ -734,10 +766,10 @@ int CFortunaStake::GetPaymentAmount(const CBlockIndex *pindex, int nMaxBlocksToS //printf("(payInfo:%d@%f)...", payCount, payRate); int64_t amount = 0; int matches = 0; - BOOST_FOREACH(PAIRTYPE(int, int64_t) &item, payData) + BOOST_FOREACH(CFortunaPayData &item, payData) { - if (item.first > pindex->nHeight - nMaxBlocksToScanBack) { // find payments in last scanrange - amount += item.second; + if (item.height > pindex->nHeight - nMaxBlocksToScanBack && mapBlockIndex.count(item.hash)) { // find payments in last scanrange + amount += item.amount; matches++; } } @@ -792,20 +824,31 @@ int CFortunaStake::UpdateLastPaidAmounts(const CBlockIndex *pindex, int nMaxBloc //if (now > pindex->GetBlockTime()) return 0; // don't update paid amounts for nodes before the block they broadcasted on if (payData.size()) { - // when operating on cache, prune old entries to keep this at exactly the last 600 blocks. + // when operating on cache, prune old entries to keep this at exactly the last 600 blocks. (note: don't do this) // if a node doesn't get any reorgs, it won't clear old payments here and the average amount will increase // then they will approve high rates, leading to other nodes who DID reorg seeing the average lower and rejecting it. - - std::vector >::iterator it = payData.begin(); - while(it != payData.end()){ - if ((*it).first > pindex->nHeight - scanBack) { // find payments in last scanrange - rewardValue += (*it).second; - rewardCount++; - ++it; - } else { - // remove it from payData - if (fDebug) { printf("Removing old payData for FS %s at height %d\n",addr.ToString().c_str(),(*it).first); } - it = payData.erase(it); + /* + std::vector >::iterator it = payData.begin(); + while(it != payData.end()){ + if ((*it).first > pindex->nHeight - scanBack) { // find payments in last scanrange + rewardValue += (*it).second; + rewardCount++; + ++it; + } else { + // remove it from payData + if (fDebug) { printf("Removing old payData for FS %s at height %d\n",addr.ToString().c_str(),(*it).first); } + it = payData.erase(it); + } + } + */ + // all of that doesn't matter if we pay attention to the hash of the payment! + BOOST_FOREACH(CFortunaPayData &item, payData) + { + if (mapBlockIndex.count(item.hash)) { + if (item.height > pindex->nHeight - nMaxBlocksToScanBack) { // find payments in last scanrange + rewardValue += item.amount; + rewardCount++; + } } } @@ -841,21 +884,28 @@ int CFortunaStake::UpdateLastPaidAmounts(const CBlockIndex *pindex, int nMaxBloc { BOOST_FOREACH(CTxOut txout, block.vtx[block.IsProofOfWork() ? 0 : 1].vout) { + if(mnpayee == txout.scriptPubKey) { int height = BlockReading->nHeight; - if (rewardCount == 0) { + int64_t amount = txout.nValue; + uint256 hash = BlockReading->GetBlockHash(); + CFortunaPayData data; + + data.height = height; + data.amount = amount; + data.hash = hash; + + // first match is the last! ;) + if (nBlockLastPaid == 0) { nBlockLastPaid = height; - nTimeLastPaid = BlockReading->nTime; } - // values - val = txout.nValue; - // add this profit & the reward itself - rewardValue += val; + // add this profit & the reward itself so we can update the node + rewardValue += amount; rewardCount++; // make a note in the node for later lookup - payData.push_back(make_pair(height, val)); + payData.push_back(data); } } } @@ -919,7 +969,6 @@ void CFortunaStake::UpdateLastPaidBlock(const CBlockIndex *pindex, int nMaxBlock BOOST_FOREACH(CTxOut txout, block.vtx[0].vout) if(mnpayee == txout.scriptPubKey) { nBlockLastPaid = BlockReading->nHeight; - nTimeLastPaid = BlockReading->nTime; int lastPay = pindexBest->nHeight - nBlockLastPaid; int value = txout.nValue; // TODO HERE: Check the nValue for the fortunastake payment amount @@ -931,8 +980,7 @@ void CFortunaStake::UpdateLastPaidBlock(const CBlockIndex *pindex, int nMaxBlock // TODO HERE: Scan the block for fortunastake payment amount BOOST_FOREACH(CTxOut txout, block.vtx[1].vout) if(mnpayee == txout.scriptPubKey) { - nBlockLastPaid = BlockReading->nHeight; - nTimeLastPaid = BlockReading->nTime; + nBlockLastPaid = BlockReading->nHeight;\ int lastPay = pindexBest->nHeight - nBlockLastPaid; int value = txout.nValue; // TODO HERE: Check the nValue for the fortunastake payment amount @@ -1296,3 +1344,279 @@ bool CFortunastakePayments::SetPrivKey(std::string strPrivKey) return false; } } + +struct MatchPubkey +{ + MatchPubkey(const CScript& s) : s_(s) {} + bool operator()(const CFortunaStake& mn) const + { + return GetScriptForDestination(mn.pubkey.GetID()) == s_; + } + private: + const CScript& s_; +}; + +void CFortunaPayments::update(const CBlockIndex *pindex, bool force) +{ + if (!pindex || IsInitialBlockDownload()) return 0; + const CBlockIndex *BlockReading = pindex; + int rewardCount = 0; + int64_t rewardValue = 0; + int64_t val = 0; + int scanBack = max(FORTUNASTAKE_FAIR_PAYMENT_MINIMUM, (int)mnCount) * FORTUNASTAKE_FAIR_PAYMENT_ROUNDS; + + int64_t nStart = GetTimeMillis(); + + // situations we want to force update: + // - update is called from GetForunstakeRanks which is called with new pindex, or pindexBest + // we only want to update this on a reorg + if (force) { + LOCK(cs_fortunastakes); + + // clear existing pay data + BOOST_FOREACH(CFortunaStake& mn, vecFortunastakes) + { + mn.payData.clear(); + } + + // do the loop and fill all the payments in + for (int i = 0; i < scanBack; i++) { + val = 0; + CBlock block; + if(!block.ReadFromDisk(BlockReading, true)) // shouldn't really happen + continue; + + bool found = false; + // if it's a legit block, then count it against this node and record it in a vector + if (block.IsProofOfWork() || block.IsProofOfStake()) + { + BOOST_FOREACH(CTxOut txout, block.vtx[block.IsProofOfWork() ? 0 : 1].vout) + { + BOOST_FOREACH(CFortunaStake& mn, vecFortunastakes) + { + if (GetScriptForDestination(mn.pubkey.GetID()) == txout.scriptPubKey) + { + int height = BlockReading->nHeight; + int64_t amount = txout.nValue; + uint256 hash = BlockReading->GetBlockHash(); + CFortunaPayData data; + + data.height = height; + data.amount = amount; + data.hash = hash; + mn.payData.push_back(data); + + // first match is the last! ;) + if (mn.nBlockLastPaid == 0) { + mn.nBlockLastPaid = height; + } + found = true; + break; + } + } + if (found) break; + } + } + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + BlockReading = BlockReading->pprev; + } + + } + if (fDebug) printf("Calculating payrates (%d ms)\n",GetTimeMillis() - nStart); + + // do pay rate loops + BOOST_FOREACH(CFortunaStake& mn, vecFortunastakes) + { + mn.SetPayRate(pindex->nHeight); + } + + if (fDebug) printf("Finished FS payments. (%d ms)\n",GetTimeMillis() - nStart); + +} + +bool CFortunaPayments::initialize(const CBlockIndex *pindex) +{ + if (vCollaterals.size() > 0) return; + printf("Setting up FS payment validation...\n"); + CTxDB txdb("r"); + const CBlockIndex *BlockReading = pindex; + int blocksFound = 0; + int nHeight = 0; + for (int i = 0; BlockReading && BlockReading->nHeight > BLOCK_START_FORTUNASTAKE_PAYMENTS; i++) { + CBlock block; + if(!block.ReadFromDisk(BlockReading, true)) // shouldn't really happen + continue; + + nHeight = BlockReading->nHeight; + + if (block.IsProofOfWork() || block.IsProofOfStake()) + { + BOOST_FOREACH(const CTransaction& tx, block.vtx) { + int n = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + if(txout.nValue == GetMNCollateral() * COIN) { + COutPoint cout = COutPoint(tx.GetHash(),n); + CTxIn vin = CTxIn(cout, txout.scriptPubKey); + CTxOut vout = CTxOut(1 * COIN, txout.scriptPubKey); + CScript mnpayee = GetScriptForDestination(txout.scriptPubKey.GetID()); + CTransaction txCollateral; + txCollateral.vin.push_back(vin); + txCollateral.vout.push_back(vout); + + CFortunaCollateral data; + data.vin = vin; + data.blockHash = block.GetHash(); + data.height = nHeight; + data.scriptPubKey = txout.scriptPubKey; + + // if data is already in the vector for this script, let's just skip + if (std::find(vScripts.begin(), vScripts.end(), mnpayee) != vScripts.end()) { continue; } + + //if (fDebug) printf("Found FS payment at height %d - TX %s\n TXOut %s\n",nHeight,tx.ToString().c_str(),txout.ToString().c_str()); + // TODO: check spent with fetch inputs? + bool* pfMissingInputs; + //if(fDebug) printf("CForTunaPool::IsCollateralValid - Testing TX %s\n",txCollateral.ToString().c_str()); + if(!AcceptableInputs(mempool, txCollateral, false, pfMissingInputs)){ + //if(fDebug) printf("CForTunaPool::IsCollateralValid - didn't pass IsAcceptable\n"); + continue; + } else { + // show addy? + if(fDebug) printf("CForTunaPool::IsCollateralValid - Valid FS Collateral found for outpoint %s\n",vin.ToString().c_str()); + + vCollaterals.push_back(data); + vScripts.push_back(mnpayee); + + } + } + n++; + } + } + } + + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + + BlockReading = BlockReading->pprev; + } + + if (fDebug){ + printf("finished at height %d\n-----------%d collaterals------------",nHeight,vCollaterals.size()); + BOOST_FOREACH(CFortunaCollateral& rec, vCollaterals) + { + CTxDestination address1; + ExtractDestination(rec.scriptPubKey, address1); + CBitcoinAddress address2(address1); + printf("Height %d: MN Address %s secured by collateral %s\n",rec.height,address2.ToString().c_str(),rec.vin.ToString().c_str()); + } + } + + return (blocksFound > 0); + +} + +bool FindFSPayment(CScript& payee, CBlockIndex* pindex) +{ + if (fDebug) printf("Searching for FS collateral...\n"); + CTxDB txdb("r"); + const CBlockIndex *BlockReading = pindex; + int blocksFound = 0; + int nHeight = 0; + for (int i = 0; BlockReading && BlockReading->nHeight > 1; i++) { + CBlock block; + if(!block.ReadFromDisk(BlockReading, true)) // shouldn't really happen + continue; + + nHeight = BlockReading->nHeight; + + if (block.IsProofOfWork() || block.IsProofOfStake()) + { + bool found = false; + BOOST_FOREACH(const CTransaction& tx, block.vtx) { + if (tx.IsCoinBase()) continue; + int n = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + if(payee == txout.scriptPubKey && txout.nValue == GetMNCollateral() * COIN) { + if (fDebug) printf("Found FS payment at height %d - to %s\n",nHeight,txout.ToString().c_str()); + // TODO: check spent with fetch inputs? + CTransaction txCollateral; + COutPoint cout = COutPoint(tx.GetHash(),n); + CTxOut vout = CTxOut(1 * COIN, txout.scriptPubKey); + CTxIn vin = CTxIn(cout, txout.scriptPubKey); + txCollateral.vin.push_back(vin); + txCollateral.vout.push_back(vout); + //if(fDebug) printf("CForTunaPool::IsCollateralValid - Testing TX %s\n",txCollateral.ToString().c_str()); + bool* pfMissingInputs; + if(!AcceptableInputs(mempool, txCollateral, false, pfMissingInputs)){ + if(fDebug) printf("CForTunaPool::IsCollateralValid - didn't pass IsAcceptable\n"); + continue; + } else { + found = true; + return true; + } + + } + n++; + } + if (found) return true; + } + } + + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + + BlockReading = BlockReading->pprev; + } + printf("finished at height %d\n",nHeight); + return (blocksFound > 0); + +} + +bool FindFSPayments(CScript& payee, CBlockIndex* pindex) +{ + printf("FSPayment:"); + CTxDB txdb("r"); + const CBlockIndex *BlockReading = pindex; + int blocksFound = 0; + int nHeight = 0; + for (int i = 0; BlockReading && BlockReading->nHeight > MN_ENFORCEMENT_ACTIVE_HEIGHT; i++) { + CBlock block; + if(!block.ReadFromDisk(BlockReading, true)) // shouldn't really happen + continue; + + nHeight = BlockReading->nHeight; + + if (block.IsProofOfWork() || block.IsProofOfStake()) + { + BOOST_FOREACH(const CTransaction& tx, block.vtx) { + int n = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + if(payee == txout.scriptPubKey && txout.nValue == GetMNCollateral() * COIN) { + if (fDebug) printf("Found FS payment at height %d - to %s\n",nHeight,txout.ToString().c_str()); + // TODO: check spent with fetch inputs? + CTransaction txCollateral; + CTxOut vout = CTxOut((GetMNCollateral() - 1)* COIN, forTunaPool.collateralPubKey); + CTxIn vin = CTxIn(txout.GetHash(),n); + txCollateral.vin.push_back(vin); + txCollateral.vout.push_back(vout); + bool* pfMissingInputs; + if(!AcceptableInputs(mempool, txCollateral, false, pfMissingInputs)){ + if(fDebug) printf("CForTunaPool::IsCollateralValid - didn't pass IsAcceptable\n"); + continue; + } + return true; + + } + n++; + } + } + } + + if (BlockReading->pprev == NULL) { assert(BlockReading); break; } + + BlockReading = BlockReading->pprev; + } + printf("finished at height %d\n",nHeight); + return (blocksFound > 0); + +} diff --git a/src/fortunastake.h b/src/fortunastake.h index 2299dc7f..7feb7302 100644 --- a/src/fortunastake.h +++ b/src/fortunastake.h @@ -65,6 +65,67 @@ int CountFortunastakesAboveProtocol(int protocolVersion); void ProcessMessageFortunastake(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); bool CheckFortunastakeVin(CTxIn& vin, std::string& errorMessage, CBlockIndex *pindex); +// For storing payData +class CFortunaPayData +{ +public: + int height; + uint256 hash; + int64_t amount; + + CFortunaPayData() { + height = 0; + hash = 0; + amount = 0; + } + +}; + +class CFortunaCollateral +{ +public: + CTxIn vin; + CScript scriptPubKey; + int height; + uint256 blockHash; + + CFortunaCollateral() { + height = 0; + blockHash = 0; + } +}; + +// For storing payData +class CFortunaPayments +{ +public: + std::vector vStakes; // this array should be sorted + std::vector vPayments; // this array just contains our scanned data + //std::vector vCollaterals; + std::vector vCollaterals; + std::vector vScripts; + + CFortunaPayments() { + // fill vStakes array with pointers to MN's from vecFortunastakes + } + + bool add(CFortunaStake* mn) + { + // add address of pointer into the payments array + return true; + } + + bool remove(CFortunaStake* mn) + { + // remove address of pointer from the payments array + return true; + } + + void update(const CBlockIndex *pindex, bool force = false); + + bool initialize(const CBlockIndex* pindex); +}; + // // The Fortunastake Class. For managing the fortuna process. It contains the input of the 5000 D, signature to prove // it's the one who own that ip address and code for calculating the payment election. @@ -79,7 +140,7 @@ class CFortunaStake CPubKey pubkey; CPubKey pubkey2; std::vector sig; - std::vector > payData; + std::vector payData; pair payInfo; int64_t payRate; int payCount; @@ -96,7 +157,6 @@ class CFortunaStake int64_t lastTimeChecked; int nBlockLastPaid; int64_t nTimeLastChecked; - int64_t nTimeLastPaid; int64_t nTimeRegistered; int nRank; @@ -123,9 +183,11 @@ class CFortunaStake lastTimeChecked = 0; nBlockLastPaid = 0; nTimeLastChecked = 0; - nTimeLastPaid = 0; nTimeRegistered = newNow; nRank = 0; + payValue = 0; + payRate = 0; + payCount = 0; } uint256 CalculateScore(int mod=1, int64_t nBlockHeight=0); @@ -146,10 +208,12 @@ class CFortunaStake } } - int IsActive(CBlockIndex* pindex) { - return ((lastDseep > (GetAdjustedTime() - FORTUNASTAKE_MIN_DSEEP_SECONDS)) && - (lastTimeSeen - now > (max(FORTUNASTAKE_FAIR_PAYMENT_MINIMUM, (int)mnCount) * 30)) - ); // dsee broadcast is more than a round old & active from dseeps + bool IsActive() { + if (lastTimeSeen - now > (max(FORTUNASTAKE_FAIR_PAYMENT_MINIMUM, (int)mnCount) * 30)) + { // dsee broadcast is more than a round old, let's consider it active + return true; + } + return false; } inline uint64_t SliceHash(uint256& hash, int slice) @@ -202,6 +266,7 @@ int GetFortunastakeRank(CFortunaStake& tmn, CBlockIndex* pindex, int minProtocol int GetFortunastakeByRank(int findRank, int64_t nBlockHeight=0, int minProtocol=CFortunaStake::minProtoVersion); bool GetFortunastakeRanks(CBlockIndex* pindex=pindexBest); extern int64_t nAverageFSIncome; +bool FindFSPayment(CScript& payee, CBlockIndex* pindex=pindexBest); // for storing the winning payments class CFortunastakePaymentWinner diff --git a/src/init.cpp b/src/init.cpp index bccbe87e..bb13ae49 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -465,8 +465,8 @@ bool AppInit2() nNodeLifespan = GetArg("-addrlifespan", 7); fUseFastIndex = GetBoolArg("-fastindex", true); - nMinStakeInterval = GetArg("-minstakeinterval", 30); - nMinerSleep = GetArg("-minersleep", 30000); //500 + nMinStakeInterval = GetArg("-minstakeinterval", 60); // 2 blocks, don't make pos chains! + nMinerSleep = GetArg("-minersleep", 10000); //default 10seconds, higher=more cpu usage // Largest block you're willing to create. // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity: @@ -586,6 +586,7 @@ bool AppInit2() fDebugRingSig = GetBoolArg("-debugringsig"); fNoSmsg = GetBoolArg("-nosmsg"); + fDisableStealth = GetBoolArg("-disablestealth"); // force-disable stealth transaction scanning bitdb.SetDetach(GetBoolArg("-detachdb", false)); diff --git a/src/main.cpp b/src/main.cpp index 35d5450b..d7b86e17 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -48,11 +48,11 @@ unsigned int nTargetSpacing = 30; // 30 seconds, FAST unsigned int nStakeMinAge = 8 * 60 * 60; // 8 hour min stake age unsigned int nStakeMaxAge = -1; // unlimited unsigned int nModifierInterval = 10 * 60; // time to elapse before new modifier is computed -int64_t nLastCoinStakeSearchTime = 0; +int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); int nCoinbaseMaturity = 20; //30 on Mainnet D e n a r i u s, 20 for testnet CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; -bool FortunaReorgBlock; +bool FortunaReorgBlock = true; uint256 nBestChainTrust = 0; uint256 nBestInvalidTrust = 0; @@ -265,7 +265,11 @@ bool AbortNode(const std::string &strMessage, const std::string &userMessage) { return false; } - +bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) +{ + // TODO: + return false; +} ////////////////////////////////////////////////////////////////////////////// // @@ -948,7 +952,12 @@ bool CTxMemPool::accept(CTxDB& txdb, CTransaction &tx, if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) { if (fInvalid) - return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); + { + if (fDebug) + return error("CTxMemPool::accept() : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); + else return false; + } + if (pfMissingInputs) *pfMissingInputs = true; return false; @@ -1108,7 +1117,8 @@ bool AcceptableInputs(CTxMemPool& pool, const CTransaction &txo, bool fLimitFree if (!tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid)) { if (fInvalid) - return error("AcceptableInputs : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); + if (fDebugNet) return error("AcceptableInputs : FetchInputs found invalid tx %s", hash.ToString().substr(0,10).c_str()); + return false; if (pfMissingInputs) *pfMissingInputs = true; return false; @@ -1689,7 +1699,11 @@ bool IsInitialBlockDownload() pindexBest->GetBlockTime() < (GetTime() - 300)); // last block is more than 5 minutes old if (state) + { lockIBDState = true; + // do stuff required at end of sync + GetFortunastakeRanks(pindexBest); + } return state; } @@ -1864,7 +1878,10 @@ bool CTransaction::FetchInputs(CTxDB& txdb, const map& mapTes // Revisit this if/when transaction replacement is implemented and allows // adding inputs: fInvalid = true; - return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); + if (fDebugNet) + return DoS(100, error("FetchInputs() : %s prevout.n out of range %d %"PRIszu" %"PRIszu" prev tx %s\n%s", GetHash().ToString().substr(0,10).c_str(), prevout.n, txPrev.vout.size(), txindex.vSpent.size(), prevout.hash.ToString().substr(0,10).c_str(), txPrev.ToString().c_str())); + return DoS(100, false); + } } @@ -2493,7 +2510,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) } } - if(!FortunaReorgBlock && !fJustCheck && pindex->GetBlockTime() > GetTime() - 20*nCoinbaseMaturity && (pindex->nHeight < pindexBest->nHeight+5) && !IsInitialBlockDownload() && FortunastakePayments == true) + if(!fJustCheck && pindex->GetBlockTime() > GetTime() - 20*nCoinbaseMaturity && (pindex->nHeight < pindexBest->nHeight+5) && !IsInitialBlockDownload() && FortunastakePayments == true) { LOCK2(cs_main, mempool.cs); @@ -2505,6 +2522,8 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) if(IsProofOfStake() && pindexBest != NULL){ if(pindexBest->GetBlockHash() == hashPrevBlock){ + // make sure the ranks are updated to prev block + GetFortunastakeRanks(pindexBest); // Calculate Coin Age for Fortunastake Reward Calculation uint64_t nCoinAge; if (!vtx[1].GetCoinAge(txdb, nCoinAge)) @@ -2556,21 +2575,7 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) if (vtx[1].vout[i].scriptPubKey == pubScript) { int64_t value = vtx[1].vout[i].nValue; - int lastPaid = mn.nBlockLastPaid; - int vinAge = mn.GetFortunastakeInputAge(pindex); - int rank = (GetFortunastakeRank(mn, pindex)); - int maxrank = mnCount-(mnCount/10); - if (fDebug) printf("CheckBlock-POS() : Fortunastake PoS payee found at block %d: %s who got paid %s D rate:%d age: %d, rank %d\n", pindex->nHeight, address2.ToString().c_str(), FormatMoney(value).c_str(), mn.payRate, vinAge, rank); - - if(vinAge < (nBestHeight > BLOCK_START_FORTUNASTAKE_DELAYPAY ? FORTUNASTAKE_MIN_CONFIRMATIONS_NOPAY : FORTUNASTAKE_MIN_CONFIRMATIONS)) // if MN is too new - { - if (pindexBest->nHeight >= MN_ENFORCEMENT_ACTIVE_HEIGHT) { - return error("CheckBlock-POS() : Fortunastake has only %d confirmations (requires %d) - rejecting block.", vinAge, FORTUNASTAKE_MIN_CONFIRMATIONS_NOPAY); - } else { - if (fDebug) printf("WARNING: Fortunastake has only %d confirmations (requires %d) and will not be accepted after block %d\n", vinAge, FORTUNASTAKE_MIN_CONFIRMATIONS_NOPAY, MN_ENFORCEMENT_ACTIVE_HEIGHT); - } - break; - } + if (fDebug) printf("CheckBlock-POS() : Fortunastake PoS payee found at block %d: %s who got paid %s D rate:%"PRId64" rank:%d lastpaid:%d\n", pindex->nHeight, address2.ToString().c_str(), FormatMoney(value).c_str(), mn.payRate, mn.nRank, mn.nBlockLastPaid); if (!fIsInitialDownload) { if (!CheckFSPayment(pindex, vtx[1].vout[i].nValue, mn)) // if MN is being paid and it's bottom 50% ranked, don't let it be paid. @@ -2589,13 +2594,24 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) } // add mn payment data mn.nBlockLastPaid = pindex->nHeight; - mn.payData.push_back(make_pair(pindex->nHeight, value)); + CFortunaPayData data; + data.height = pindex->nHeight; + data.amount = value; + data.hash = pindex->GetBlockHash(); + mn.payData.push_back(data); mn.SetPayRate(pindex->nHeight); foundPayee = true; paymentOK = true; break; } } + // if payee not found in mn list, check if the pubkey holds a 5K transaction + if (!foundPayee) { + if (FindFSPayment(payee, pindex)) { + if (fDebug) printf("CheckBlock-POS() : WARNING: Payee was not found in MN list, but confirmed to hold collateral.\n"); + foundPayee = true; + } + } } } } @@ -2643,6 +2659,10 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) } }else if(IsProofOfWork() && pindexBest != NULL){ if(pindexBest->GetBlockHash() == hashPrevBlock){ + + // make sure the ranks are updated + GetFortunastakeRanks(pindexBest); + int64_t fortunastakePaymentAmount = GetFortunastakePayment(pindex->nHeight, vtx[0].GetValueOut()); // If we don't already have its previous block, skip fortunastake payment step @@ -2681,29 +2701,13 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) if (payee == pubScript) { - int lastPaid = mn.nBlockLastPaid; - int vinAge = mn.GetFortunastakeInputAge(pindex); - int rank = (GetFortunastakeRank(mn, pindex)); - int maxrank = mnCount - (mnCount/10); - - if (fDebug) printf("CheckBlock-POW() : Fortunastake PoW payee found at block %d: %s who got paid %s D rate:%d age: %d, rank %d\n", pindex->nHeight+1, address2.ToString().c_str(), FormatMoney(vtx[0].vout[i].nValue).c_str(), FormatMoney(mn.payRate).c_str(), vinAge, rank); - - if(vinAge < (nBestHeight > BLOCK_START_FORTUNASTAKE_DELAYPAY ? FORTUNASTAKE_MIN_CONFIRMATIONS_NOPAY : FORTUNASTAKE_MIN_CONFIRMATIONS)) // if MN is too new - { - if (pindexBest->nHeight >= MN_ENFORCEMENT_ACTIVE_HEIGHT) { - return error("CheckBlock-POW() : Fortunastake has only %d confirmations (requires %d) - rejecting block.", vinAge, FORTUNASTAKE_MIN_CONFIRMATIONS_NOPAY); - } else { - if (fDebug) printf("WARNING: Fortunastake has only %d confirmations (requires %d) and will not be accepted after block %d\n", vinAge ,FORTUNASTAKE_MIN_CONFIRMATIONS_NOPAY, MN_ENFORCEMENT_ACTIVE_HEIGHT); - } - break; - } - + if (fDebug) printf("CheckBlock-POW() : Fortunastake PoW payee found at block %d: %s who got paid %s D rate:%"PRId64" rank:%d lastpaid:%d\n", pindex->nHeight, address2.ToString().c_str(), FormatMoney(vtx[0].vout[i].nValue).c_str(), FormatMoney(mn.payRate).c_str(), mn.nRank, mn.nBlockLastPaid); if (!fIsInitialDownload) { if (!CheckFSPayment(pindex, vtx[0].vout[i].nValue, mn)) // if MN is being paid and it's bottom 50% ranked, don't let it be paid. { if (pindexBest->nHeight >= MN_ENFORCEMENT_ACTIVE_HEIGHT) { - return error("CheckBlock-POW() : Fortunastake overpayment detected, rejecting block. rank:%d payRate:%d",rank,FormatMoney(mn.payRate).c_str()); + return error("CheckBlock-POW() : Fortunastake overpayment detected, rejecting block. rank:%d value:%s avg:%s payRate:%s",mn.nRank,FormatMoney(mn.payValue).c_str(),FormatMoney(nAverageFSIncome).c_str(),FormatMoney(mn.payRate).c_str()); } else { if (fDebug) printf("WARNING: This fortunastake payment is too aggressive and will not be accepted after block %d\n", MN_ENFORCEMENT_ACTIVE_HEIGHT); @@ -2716,7 +2720,11 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) } mn.nBlockLastPaid = pindex->nHeight; - mn.payData.push_back(make_pair(pindex->nHeight, vtx[0].vout[i].nValue)); + CFortunaPayData data; + data.height = pindex->nHeight; + data.amount = vtx[0].vout[i].nValue; + data.hash = pindex->GetBlockHash(); + mn.payData.push_back(data); mn.SetPayRate(pindex->nHeight); foundPayee = true; paymentOK = true; @@ -2726,6 +2734,14 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) foundPayee = true; } } + + // if payee not found in mn list, check if the pubkey holds a 5K transaction + if (!foundPayee) { + if (FindFSPayment(payee, pindex)) { + if (fDebug) printf("CheckBlock-POW() : WARNING: Payee was not found in MN list, but confirmed to hold collateral.\n"); + foundPayee = true; + } + } } } @@ -2869,7 +2885,6 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) { printf("REORGANIZE\n"); - FortunaReorgBlock = true; // Find the fork CBlockIndex* pfork = pindexBest; CBlockIndex* plonger = pindexNew; @@ -2966,13 +2981,7 @@ bool static Reorganize(CTxDB& txdb, CBlockIndex* pindexNew) mempool.removeConflicts(tx); } - BOOST_FOREACH(CFortunaStake& mn, vecFortunastakes) // now clear the ranks because we need to reorganize the payments in case extras got in - { - mn.payData.clear(); - } - if (!IsInitialBlockDownload()) GetFortunastakeRanks(pindex); // recalculate ranks for the this block hash if required - - FortunaReorgBlock = false; + FortunaReorgBlock = true; printf("REORGANIZE: done\n"); return true; @@ -3481,6 +3490,7 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) { AssertLockHeld(cs_main); + int64_t nStartTime = GetTimeMillis(); // Check for duplicate uint256 hash = pblock->GetHash(); if (mapBlockIndex.count(hash)) @@ -3602,7 +3612,11 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) mapOrphanBlocksByPrev.erase(hashPrev); } - if (fDebug) printf("ProcessBlock: ACCEPTED\n"); + if (fDebug && GetBoolArg("-showtimers", false)) { + printf("ProcessBlock: ACCEPTED (%"PRId64"ms)\n", GetTimeMillis() - nStartTime); + } else { + if (fDebug) printf("ProcessBlock: ACCEPTED\n"); + } //After block 1.5m, The Minimum FortunaStake Protocol Version is 31005 if(nBestHeight >= 1500000) { @@ -3629,18 +3643,23 @@ bool CBlock::SignBlock(CWallet& wallet, int64_t nFees) if (IsProofOfStake()) return true; - nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp + // nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp + // nLastCoinStakeSearchTime = pindexBest->GetBlockTime(); // time of the last block in our index CKey key; - CTransaction txCoinStake; + CTransaction txCoinStake; // make a new transaction. int64_t nSearchTime = txCoinStake.nTime; // search to current time + if (fDebug && GetBoolArg("-printcoinstake")) printf ("searchtime %ld to %ld \n",nSearchTime,nLastCoinStakeSearchTime); if (nSearchTime > nLastCoinStakeSearchTime) { + if (fDebug && GetBoolArg("-printcoinstake")) printf ("nSearchTime %ld > nLastCoinStakeSearchTime %ld\n",nSearchTime,nLastCoinStakeSearchTime); if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, nFees, txCoinStake, key)) { + if (fDebug && GetBoolArg("-printcoinstake")) printf ("CreateCoinStake succeeded \n"); if (txCoinStake.nTime >= max(pindexBest->GetPastTimeLimit()+1, PastDrift(pindexBest->GetBlockTime()))) { + if (fDebug && GetBoolArg("-printcoinstake")) printf ("txCoinStake.nTime >= max(pindexBest->GetPastTimeLimit()+1, PastDrift(pindexBest->GetBlockTime()))"); // make sure coinstake would meet timestamp protocol // as it would be the same as the block timestamp vtx[0].nTime = nTime = txCoinStake.nTime; @@ -3661,6 +3680,7 @@ bool CBlock::SignBlock(CWallet& wallet, int64_t nFees) } nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; nLastCoinStakeSearchTime = nSearchTime; + if (fDebug && GetBoolArg("-printcoinstake")) printf ("CreateCoinStake failed at %ld. Try again in %ld\n",nLastCoinStakeSearchTime,nLastCoinStakeSearchInterval); } return false; @@ -4568,7 +4588,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // Send the rest of the chain if (pindex) pindex = pindex->pnext; - int nLimit = 500; + int nLimit = 1000; if (fDebugNet) printf("getblocks %d to %s limit %d\n", (pindex ? pindex->nHeight : -1), hashStop.ToString().substr(0,20).c_str(), nLimit); for (; pindex; pindex = pindex->pnext) { @@ -4855,6 +4875,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, if (pingUsecTime > 0) { // Successful ping time measurement, replace previous pfrom->nPingUsecTime = pingUsecTime; + if (fDebug) { printf("Ping time for peer %s: %d msec\n", pfrom->addr.ToString().c_str(), (((double)pfrom->nPingUsecTime) / 1e6)); } } else { // This should never happen sProblem = "Timing mishap"; @@ -5165,6 +5186,19 @@ bool SendMessages(CNode* pto, bool fSendTrickle) } + // + // Message: getblocks + // + + int n = pto->getBlocksIndex.size(); + for (int i = 0; i < n; i++) + { + if (fDebugNet) printf("Pushing getblocks %s to %s\n\n",pto->getBlocksIndex[i]->ToString().c_str(),pto->getBlocksHash[i].ToString().c_str()); + pto->PushMessage("getblocks", CBlockLocator(pto->getBlocksIndex[i]), pto->getBlocksHash[i]); + } + pto->getBlocksIndex.clear(); + pto->getBlocksHash.clear(); + // // Message: inventory // diff --git a/src/main.h b/src/main.h index b1273831..dcfb2e5c 100644 --- a/src/main.h +++ b/src/main.h @@ -208,7 +208,8 @@ int64_t GetFortunastakePayment(int nHeight, int64_t blockValue); bool IsStandardTx(const CTransaction& tx, std::string& reason); bool IsFinalTx(const CTransaction &tx, int nBlockHeight = 0, int64_t nBlockTime = 0); - +/** Get statistics from node state */ +bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); bool GetWalletFile(CWallet* pwallet, std::string &strWalletFileOut); diff --git a/src/miner.cpp b/src/miner.cpp index c170289f..1dc024f8 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -187,7 +187,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees) bFortunaStakePayment = true; } } - if(fDebug) { printf("CreateNewBlock(): Fortunastake Payments : %i\n", bFortunaStakePayment); } + if(fDebug && fDebugFS) { printf("CreateNewBlock(): Fortunastake Payments : %i\n", bFortunaStakePayment); } } // Fee-per-kilobyte amount considered the same as "free" @@ -226,7 +226,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees) } } if (found) { - printf("CreateNewBlock: Found a fortunastake to pay: %s\n",payee.ToString(true).c_str()); + if (fDebug && fDebugFS) printf("CreateNewBlock: Found a fortunastake to pay: %s\n",payee.ToString(true).c_str()); } else { printf("CreateNewBlock: Failed to detect fortunastake to pay\n"); // pay the burn address if it can't detect @@ -242,7 +242,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees) if(hasPayment){ payments = txNew.vout.size() + 1; - printf("CreateNewBlock(): Payment Size: %i\n", payments); + if (fDebug && fDebugNet) printf("CreateNewBlock(): Payment Size: %i\n", payments); pblock->vtx[0].vout.resize(payments); pblock->vtx[0].vout[payments-1].scriptPubKey = payee; @@ -252,7 +252,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees) ExtractDestination(payee, address1); CBitcoinAddress address2(address1); - printf("CreateNewBlock(): Fortunastake payment to %s\n", address2.ToString().c_str()); + if (fDebug && fDebugFS) printf("CreateNewBlock(): Fortunastake payment to %s\n", address2.ToString().c_str()); } } @@ -696,8 +696,10 @@ void StakeMiner(CWallet *pwallet) fTryToSync = false; if (vNodes.size() < 3 || nBestHeight < GetNumBlocksOfPeers()) { + if (fDebug && GetBoolArg("-printcoinstake")) + printf("StakeMiner() vNodes.size() < 3 || nBestHeight < GetNumBlocksOfPeers()\n"); vnThreadsRunning[THREAD_STAKE_MINER]--; - MilliSleep(60000); + MilliSleep(5000); vnThreadsRunning[THREAD_STAKE_MINER]++; if (fShutdown) return; @@ -714,14 +716,15 @@ void StakeMiner(CWallet *pwallet) if (nMinStakeInterval > 0 && nTimeLastStake + (int64_t)nMinStakeInterval > GetTime()) { - if (fDebug) + if (fDebug && GetBoolArg("-printcoinstake")) printf("StakeMiner() Rate limited to 1 / %d seconds.\n", nMinStakeInterval); - MilliSleep(nMinStakeInterval * 500); // nMinStakeInterval / 2 seconds + MilliSleep(nMinStakeInterval * 1000); // nMinStakeInterval / 2 seconds continue; }; - if (vecFortunastakes.size() == 0 || (mnCount > 0 && vecFortunastakes.size() < mnCount)) + if (vecFortunastakes.size() == 0) { + if (fDebug && GetBoolArg("-printcoinstake")) printf("StakeMiner() waiting for FS list."); vnThreadsRunning[THREAD_STAKE_MINER]--; MilliSleep(10000); vnThreadsRunning[THREAD_STAKE_MINER]++; @@ -732,24 +735,33 @@ void StakeMiner(CWallet *pwallet) // Create new block // int64_t nFees; + if (fDebug && GetBoolArg("-printcoinstake")) printf ("creating block. "); auto_ptr pblock(CreateNewBlock(pwallet, true, &nFees)); if (!pblock.get()) return; + if (fDebug && GetBoolArg("-printcoinstake")) printf ("signing block. "); // Trying to sign a block if (pblock->SignBlock(*pwallet, nFees)) { + if (fDebug && GetBoolArg("-printcoinstake")) printf ("checking stake. "); bool staked; SetThreadPriority(THREAD_PRIORITY_NORMAL); staked = CheckStake(pblock.get(), *pwallet); + if (staked && fDebug && GetBoolArg("-printcoinstake")) printf ("stake is good. \n"); SetThreadPriority(THREAD_PRIORITY_LOWEST); if (fShutdown) return; MilliSleep(nMinerSleep); - if (staked) MilliSleep(nMinerSleep*10); // sleep for a while after successfully staking + if (staked) { + nTimeLastStake = GetAdjustedTime(); + MilliSleep(nMinerSleep*3); // sleep for a while after successfully staking + } + else if (fDebug && GetBoolArg("-printcoinstake")) printf ("stake is bad. \n"); } else { + if (fDebug && GetBoolArg("-printcoinstake")) printf ("failed to sign.\n"); if (fShutdown) return; MilliSleep(nMinerSleep); diff --git a/src/net.cpp b/src/net.cpp index d7809743..a5237b41 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -110,7 +110,10 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) pindexLastGetBlocksBegin = pindexBegin; hashLastGetBlocksEnd = hashEnd; - PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); + getBlocksIndex.push_back(pindexBegin); + getBlocksHash.push_back(hashEnd); + + //PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); } @@ -1810,7 +1813,10 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu // for now, use a very simple selection metric: the node from which we received // most recently +// patch: use pingtime, e.g. 200ms * -1 = -200, so 10ms *-1 = -10 would win. static int64_t NodeSyncScore(const CNode *pnode) { + if (pnode->nPingUsecTime > 0) + return pnode->nPingUsecTime * -1; return pnode->nLastRecv; } @@ -1883,7 +1889,7 @@ void ThreadMessageHandler2(void* parg) vNodesCopy = vNodes; BOOST_FOREACH(CNode* pnode, vNodesCopy) { pnode->AddRef(); - if (pnode == pnodeSync) + if (pnode == pnodeSync && pnode->nLastRecv > GetTime() - 5) // only accept a node who has replied in last 5 secs, if they stop then swap nodes fHaveSyncNode = true; } } diff --git a/src/net.h b/src/net.h index ed98641f..aa6f1aca 100644 --- a/src/net.h +++ b/src/net.h @@ -158,6 +158,15 @@ extern NodeId nLastNodeId; extern CCriticalSection cs_nLastNodeId; +class CNodeStateStats +{ +public: + int nMisbehavior; + int nSyncHeight; + int nCommonHeight; + std::vector vHeightInFlight; +}; + class CNodeStats { public: @@ -168,14 +177,18 @@ class CNodeStats uint64_t nSendBytes; uint64_t nRecvBytes; int64_t nTimeConnected; + int64_t nTimeOffset; std::string addrName; int nVersion; + int nTypeInd; std::string strSubVer; bool fInbound; int nStartingHeight; int nMisbehavior; + bool fSyncNode; double dPingTime; double dPingWait; + std::string addrLocal; }; @@ -308,6 +321,8 @@ class CNode CCriticalSection cs_mapRequests; uint256 hashContinue; CBlockIndex* pindexLastGetBlocksBegin; + std::vector getBlocksIndex; + std::vector getBlocksHash; uint256 hashLastGetBlocksEnd; int nStartingHeight; bool fStartSync; diff --git a/src/netbase.cpp b/src/netbase.cpp index 048c9c3c..b8b1300b 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -34,6 +34,17 @@ enum Network ParseNetwork(std::string net) { return NET_UNROUTABLE; } +std::string GetNetworkName(enum Network net) { + switch(net) + { + case NET_IPV4: return "ipv4"; + case NET_IPV6: return "ipv6"; + case NET_TOR: return "onion"; + case NET_I2P: return "i2p"; + default: return ""; + } +} + void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { size_t colon = in.find_last_of(':'); // if a : is found, and it either follows a [...], or no other : is in the string, treat it as port separator @@ -502,10 +513,10 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest { string strDest; int port = portDefault; - + if (outProxyConnectionFailed) *outProxyConnectionFailed = false; - + SplitHostPort(string(pszDest), port, strDest); SOCKET hSocket = INVALID_SOCKET; diff --git a/src/netbase.h b/src/netbase.h index 0f74fb77..1e37422d 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -130,6 +130,7 @@ class CService : public CNetAddr typedef std::pair proxyType; enum Network ParseNetwork(std::string net); +std::string GetNetworkName(enum Network net); void SplitHostPort(std::string in, int &portOut, std::string &hostOut); bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5); bool GetProxy(enum Network net, proxyType &proxyInfoOut); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 70ebed76..ddb20b21 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -228,8 +228,13 @@ int main(int argc, char *argv[]) // Put this in a block, so that the Model objects are cleaned up before // calling Shutdown(). - if (splashref) - splash.finish(&window); + QString finishMessage = QString::fromStdString(_("Done loading")); + while (splashref->message() != finishMessage) + { + MilliSleep(200); + } + + splashref->finish(&window); //make sure user has agreed to TOU window.checkTOU(); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 417c9342..9dca4e9d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -460,6 +460,8 @@ void BitcoinGUI::createActions() openInfoAction->setStatusTip(tr("Show diagnostic information")); openGraphAction = new QAction(QIcon(":/icons/connect_4"), tr("&Network Monitor"), this); openGraphAction->setStatusTip(tr("Show network monitor")); + openPeerAction = new QAction(QIcon(":/icons/connect_4"), tr("&Peers"), this); + openPeerAction->setStatusTip(tr("Show Denarius network peers")); openConfEditorAction = new QAction(QIcon(":/icons/edit"), tr("Open Wallet &Configuration File"), this); openConfEditorAction->setStatusTip(tr("Open configuration file")); openMNConfEditorAction = new QAction(QIcon(":/icons/edit"), tr("Open &Fortunastake Configuration File"), this); @@ -482,6 +484,7 @@ void BitcoinGUI::createActions() connect(openInfoAction, SIGNAL(triggered()), this, SLOT(showInfo())); connect(openRPCConsoleAction, SIGNAL(triggered()), this, SLOT(showConsole())); connect(openGraphAction, SIGNAL(triggered()), this, SLOT(showGraph())); + connect(openPeerAction, SIGNAL(triggered()), this, SLOT(showPeer())); // Open configs from menu connect(openConfEditorAction, SIGNAL(triggered()), this, SLOT(showConfEditor())); @@ -521,6 +524,7 @@ void BitcoinGUI::createMenuBar() tools->addAction(openInfoAction); tools->addAction(openRPCConsoleAction); tools->addAction(openGraphAction); + tools->addAction(openPeerAction); tools->addSeparator(); tools->addAction(openConfEditorAction); tools->addAction(openMNConfEditorAction); @@ -1058,6 +1062,12 @@ void BitcoinGUI::showGraph() showDebugWindow(); } +void BitcoinGUI::showPeer() +{ + rpcConsole->setTabFocus(RPCConsole::TAB_PEER); + showDebugWindow(); +} + void BitcoinGUI::showConfEditor() { boost::filesystem::path pathConfig = GetConfigFile(); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index afe52594..a77d8116 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -93,14 +93,14 @@ class BitcoinGUI : public QMainWindow QStackedWidget *centralWidget; OverviewPage *overviewPage; - StatisticsPage *statisticsPage; - BlockBrowser *blockBrowser; - MarketBrowser *marketBrowser; + StatisticsPage *statisticsPage; + BlockBrowser *blockBrowser; + MarketBrowser *marketBrowser; QWidget *transactionsPage; - QWidget *mintingPage; - MultisigDialog *multisigPage; - ProofOfImage *proofOfImagePage; - FortunastakeManager *fortunastakeManagerPage; + QWidget *mintingPage; + MultisigDialog *multisigPage; + ProofOfImage *proofOfImagePage; + FortunastakeManager *fortunastakeManagerPage; AddressBookPage *addressBookPage; AddressBookPage *receiveCoinsPage; MessagePage *messagePage; @@ -120,14 +120,14 @@ class BitcoinGUI : public QMainWindow QMenuBar *appMenuBar; QAction *overviewAction; - QAction *statisticsAction; - QAction *blockAction; - QAction *marketAction; + QAction *statisticsAction; + QAction *blockAction; + QAction *marketAction; QAction *historyAction; - QAction *mintingAction; - QAction *multisigAction; - QAction *proofOfImageAction; - QAction *fortunastakeManagerAction; + QAction *mintingAction; + QAction *multisigAction; + QAction *proofOfImageAction; + QAction *fortunastakeManagerAction; QAction *quitAction; QAction *sendCoinsAction; QAction *addressBookAction; @@ -147,15 +147,16 @@ class BitcoinGUI : public QMainWindow QAction *aboutQtAction; QAction *openRPCConsoleAction; - QAction *openInfoAction; + QAction *openInfoAction; QAction *openGraphAction; + QAction *openPeerAction; QAction *openConfEditorAction; QAction *openMNConfEditorAction; QSystemTrayIcon *trayIcon; Notificator *notificator; TransactionView *transactionView; - MintingView *mintingView; + MintingView *mintingView; RPCConsole *rpcConsole; QMovie *syncIconMovie; @@ -247,6 +248,7 @@ private slots: void showInfo(); void showConsole(); void showGraph(); + void showPeer(); /** Open external (default) editor with denarius.conf */ void showConfEditor(); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 1a933ed7..eb548815 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -1,6 +1,7 @@ #include "clientmodel.h" #include "guiconstants.h" #include "optionsmodel.h" +#include "peertablemodel.h" #include "addresstablemodel.h" #include "transactiontablemodel.h" @@ -17,6 +18,8 @@ ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) : QObject(parent), optionsModel(optionsModel), cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(0) { + peerTableModel = new PeerTableModel(this); + numBlocksAtStartup = -1; pollTimer = new QTimer(this); @@ -63,7 +66,7 @@ QDateTime ClientModel::getLastBlockDate() const if (pindexBest) return QDateTime::fromTime_t(pindexBest->GetBlockTime()); else - return QDateTime::fromTime_t(1393221600); // Genesis block's time + return QDateTime::fromTime_t(1497476511); // D e n a r i u s - Genesis block's time } void ClientModel::updateTimer() @@ -158,6 +161,11 @@ OptionsModel *ClientModel::getOptionsModel() return optionsModel; } +PeerTableModel *ClientModel::getPeerTableModel() +{ + return peerTableModel; +} + QString ClientModel::formatFullVersion() const { return QString::fromStdString(FormatFullVersion()); @@ -187,8 +195,6 @@ static void NotifyBlocksChanged(ClientModel *clientmodel, int nHeight, int newNu } - - static void NotifyNumConnectionsChanged(ClientModel *clientmodel, int newNumConnections) { // Too noisy: OutputDebugStringF("NotifyNumConnectionsChanged %i\n", newNumConnections); diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index a2e4f62d..7d6e72b7 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -4,6 +4,7 @@ #include class OptionsModel; +class PeerTableModel; class AddressTableModel; class TransactionTableModel; class CWallet; @@ -22,6 +23,7 @@ class ClientModel : public QObject ~ClientModel(); OptionsModel *getOptionsModel(); + PeerTableModel *getPeerTableModel(); int getNumConnections() const; int getNumBlocks() const; @@ -37,7 +39,7 @@ class ClientModel : public QObject //! Return true if client connected to Tor bool isNativeTor() const; - + //! Return true if core is doing initial block download bool inInitialBlockDownload() const; //! Return conservative estimate of total number of blocks, or 0 if unknown @@ -52,6 +54,7 @@ class ClientModel : public QObject private: OptionsModel *optionsModel; + PeerTableModel *peerTableModel; int cachedNumBlocks; int cachedNumBlocksOfPeers; diff --git a/src/qt/forms/aboutdialog.ui b/src/qt/forms/aboutdialog.ui index a8a2a55d..b77bfa3d 100644 --- a/src/qt/forms/aboutdialog.ui +++ b/src/qt/forms/aboutdialog.ui @@ -94,7 +94,7 @@ Copyright © 2009-2014 The Bitcoin developers Copyright © 2012-2014 The NovaCoin developers Copyright © 2014 The BlackCoin developers -Copyright © 2017-2018 The Denarius developers +Copyright © 2017-2019 The Denarius developers Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -120,6 +120,9 @@ This product includes software developed by the OpenSSL Project for use in the O true + + true + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse @@ -130,6 +133,9 @@ This product includes software developed by the OpenSSL Project for use in the O Join us on the forums at <a href="https://denariustalk.org" style="color: #FFF;">https://denariustalk.org</a> + + true + diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index 3a7af08e..027c7bf9 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -25,65 +25,21 @@ QFrame::Raised - - - - - - - 11 - 75 - true - - - - Wallet - - - - - - - The displayed information may be out of date. Your wallet automatically synchronizes with the Denarius network after a connection is established, but this process has not completed yet. - - - QLabel { color: red; } - - - (out of sync) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - PointingHandCursor - - - <div style="color: #fff;">(<a href="https://www.cryptopia.co.nz/Exchange/?market=D_BTC" style="color: #fff;">Trade D</a>)</div> - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 40 + + + @@ -551,21 +507,65 @@ - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 40 - - - + + + + + + + 11 + 75 + true + + + + Wallet + + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Denarius network after a connection is established, but this process has not completed yet. + + + QLabel { color: red; } + + + (out of sync) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + PointingHandCursor + + + <div style="color: #fff;">(<a href="https://www.cryptopia.co.nz/Exchange/?market=D_BTC" style="color: #fff;">Trade D</a>)</div> + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + @@ -656,6 +656,36 @@ + + + + + 11 + 75 + true + + + + Denarius News: + + + + + + + + + + Pulling News. . . + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + diff --git a/src/qt/forms/rpcconsole.ui b/src/qt/forms/rpcconsole.ui index d7a47d9c..28b83124 100644 --- a/src/qt/forms/rpcconsole.ui +++ b/src/qt/forms/rpcconsole.ui @@ -17,16 +17,13 @@ - 0 + 3 &Information - - - 12 - + @@ -47,22 +44,6 @@ - - - - IBeamCursor - - - N/A - - - Qt::PlainText - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - @@ -96,8 +77,8 @@ - - + + IBeamCursor @@ -135,6 +116,22 @@ + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + @@ -142,6 +139,13 @@ + + + + Number of connections + + + @@ -171,13 +175,6 @@ - - - - Number of connections - - - @@ -202,7 +199,7 @@ - + Native Tor On/Off @@ -228,6 +225,13 @@ + + + + Current number of blocks + + + @@ -241,13 +245,6 @@ - - - - Current number of blocks - - - @@ -271,8 +268,15 @@ - - + + + + Last block time + + + + + IBeamCursor @@ -287,15 +291,8 @@ - - - - Last block time - - - - - + + IBeamCursor @@ -323,8 +320,8 @@ - - + + 75 @@ -332,7 +329,7 @@ - Debug log file + Command-line options @@ -349,8 +346,8 @@ - - + + 75 @@ -358,7 +355,7 @@ - Command-line options + Debug log file @@ -712,6 +709,432 @@ + + + &Peers + + + + + + Qt::ScrollBarAsNeeded + + + true + + + false + + + + + + + + 0 + 0 + + + + + 300 + 32 + + + + + 10 + + + + IBeamCursor + + + Select a peer to view detailed information. + + + Qt::AlignHCenter|Qt::AlignTop + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 300 + 0 + + + + + + + Peer ID + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Direction + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Version + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + User Agent + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Services + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Starting Height + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Sync Height + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Ban Score + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Connection Time + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Last Send + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Last Receive + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Bytes Sent + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Bytes Received + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Ping Time + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Time Offset + + + + + + + IBeamCursor + + + N/A + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/qt/fortunastakemanager.cpp b/src/qt/fortunastakemanager.cpp index 158747e4..d022bf46 100644 --- a/src/qt/fortunastakemanager.cpp +++ b/src/qt/fortunastakemanager.cpp @@ -253,6 +253,7 @@ static QString seconds_to_DHMS(quint32 duration) uint256 lastNodeUpdateHash; void FortunastakeManager::updateNodeList() { + if (pindexBest == NULL) return; if (pindexBest->GetBlockHash() == lastNodeUpdateHash) return; lastNodeUpdateHash = pindexBest->GetBlockHash(); @@ -296,7 +297,7 @@ void FortunastakeManager::updateNodeList() // populate list // Address, Rank, Active, Active Seconds, Last Seen, Pub Key QTableWidgetItem *activeItem = new QTableWidgetItem(); - activeItem->setData(Qt::DisplayRole, QString::fromStdString(mn.IsActive(pindexBest) ? "Y" : "N")); + activeItem->setData(Qt::DisplayRole, QString::fromStdString(mn.IsActive() ? "Y" : "N")); QTableWidgetItem *addressItem = new QTableWidgetItem(); addressItem->setData(Qt::EditRole, QString::fromStdString(mn.addr.ToString())); SortedWidgetItem *rankItem = new SortedWidgetItem(); @@ -342,7 +343,7 @@ void FortunastakeManager::updateNodeList() found = true; nalias = QString::fromStdString(mne.getAlias()); naddr = QString::fromStdString(mne.getIp()); - if (mn.IsActive(pindexBest)) { + if (mn.IsActive()) { nstatus = QString::fromStdString("Active for payment"); } else if (mn.status == "OK") { if (mn.lastDseep > 0) { diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 235f98ce..3597333b 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -81,6 +81,66 @@ QString dateTimeStr(qint64 nTime) return dateTimeStr(QDateTime::fromTime_t((qint32)nTime)); } +QString formatDurationStr(int secs) +{ + QStringList strList; + int days = secs / 86400; + int hours = (secs % 86400) / 3600; + int mins = (secs % 3600) / 60; + int seconds = secs % 60; + + if (days) + strList.append(QString(QObject::tr("%1 d")).arg(days)); + if (hours) + strList.append(QString(QObject::tr("%1 h")).arg(hours)); + if (mins) + strList.append(QString(QObject::tr("%1 m")).arg(mins)); + if (seconds || (!days && !hours && !mins)) + strList.append(QString(QObject::tr("%1 s")).arg(seconds)); + + return strList.join(" "); +} + +QString formatServicesStr(quint64 mask) +{ + QStringList strList; + + // TODO: add spec + // Just scan the last 8 bits for now. + for (int i = 0; i < 8; i++) + { + uint64_t check = 1 << i; + if (!(mask & check)) + continue; + switch (check) + { + case NODE_NETWORK: + strList.append("NETWORK"); + break; + //case NODE_GETUTXO: + // strList.append("GETUTXO"); + // break; + default: + strList.append(QString("%1[%2]").arg("UNKNOWN").arg(check)); + }; + }; + + if (strList.size()) + return strList.join(", "); + else + return QObject::tr("None"); +} + +QString formatPingTime(double dPingTime) +{ + return dPingTime == 0 ? QObject::tr("N/A") : QString(QObject::tr("%1 ms")).arg(QString::number((int)(dPingTime * 1000), 10)); +} + +QString formatTimeOffset(int64_t nTimeOffset) +{ + return QString(QObject::tr("%1 s")).arg(QString::number((int)nTimeOffset, 10)); +} + QFont bitcoinAddressFont() { QFont font("Monospace"); @@ -568,4 +628,3 @@ void HelpMessageBox::showOrPrint() } } // namespace GUIUtil - diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 2bd8de6e..a256f103 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -28,11 +28,23 @@ namespace GUIUtil boost::filesystem::path qstringToBoostPath(const QString &path); /* Convert OS specific boost path to QString through UTF-8 */ QString boostPathToQString(const boost::filesystem::path &path); - + // Create human-readable string from date QString dateTimeStr(const QDateTime &datetime); QString dateTimeStr(qint64 nTime); + /* Convert seconds into a QString with days, hours, mins, secs */ + QString formatDurationStr(int secs); + + /* Format CNodeStats.nServices bitmask into a user-readable string */ + QString formatServicesStr(quint64 mask); + + /* Format a CNodeCombinedStats.dPingTime into a user-readable string or display N/A, if 0*/ + QString formatPingTime(double dPingTime); + + /* Format a CNodeCombinedStats.nTimeOffset into a user-readable string. */ + QString formatTimeOffset(int64_t nTimeOffset); + // Render Bitcoin addresses in monospace font QFont bitcoinAddressFont(); @@ -83,7 +95,7 @@ namespace GUIUtil // Open debug.log void openDebugLogfile(); - + // Open denarius.conf void openConfigfile(); diff --git a/src/qt/marketbrowser.cpp b/src/qt/marketbrowser.cpp index dade9f49..5dff3119 100644 --- a/src/qt/marketbrowser.cpp +++ b/src/qt/marketbrowser.cpp @@ -13,7 +13,7 @@ using namespace json_spirit; const QString kBaseUrl = "http://denarius.io/dnrusd.php"; -const QString kBaseUrl1 = "http://blockchain.info/tobtc?currency=USD&value=1"; +const QString kBaseUrl1 = "http://denarius.io/dbitcoin.php"; const QString kBaseUrl2 = "http://denarius.io/dnrmc.php"; const QString kBaseUrl3 = "http://denarius.io/dnrbtc.php"; @@ -26,6 +26,7 @@ double denarius2; double dnrmc2; double dnrbtc2; QString bitcoing; +QString dnrnewsfeed; QString dnrmarket; QString dollarg; int mode=1; @@ -110,7 +111,7 @@ if (what == kBaseUrl1) // Bitcoin Price // QNetworkReply is a QIODevice. So we read from it just like it was a file QString bitcoin = finished->readAll(); - bitcoin2 = (1 / bitcoin.toDouble()); + bitcoin2 = (bitcoin.toDouble()); bitcoin = QString::number(bitcoin2, 'f', 2); if(bitcoin > bitcoinp) { diff --git a/src/qt/marketbrowser.h b/src/qt/marketbrowser.h index 8fe732a6..566d9fe0 100644 --- a/src/qt/marketbrowser.h +++ b/src/qt/marketbrowser.h @@ -14,6 +14,7 @@ extern QString bitcoing; extern QString dollarg; extern QString dnrmarket; +extern QString dnrnewsfeed; namespace Ui { class MarketBrowser; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 669b4584..a7d445ab 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -22,6 +22,7 @@ const QString BaseURL = "http://denarius.io/dnrusd.php"; const QString BaseURL2 = "http://denarius.io/dnrbtc.php"; +const QString BaseURL3 = "http://denarius.io/newsfeed.php"; double denariusx; double dnrbtcx; @@ -112,7 +113,7 @@ OverviewPage::OverviewPage(QWidget *parent) : { ui->setupUi(this); - //PriceRequest(); + PriceRequest(); QObject::connect(&m_nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(parseNetworkResponse(QNetworkReply*))); connect(ui->refreshButton, SIGNAL(pressed()), this, SLOT( PriceRequest())); @@ -136,6 +137,7 @@ void OverviewPage::PriceRequest() { getRequest(BaseURL); getRequest(BaseURL2); + getRequest(BaseURL3); updateDisplayUnit(); //Maybe not? } @@ -179,6 +181,16 @@ if (what == BaseURL2) // Denarius BTC Price bitcoing = dnrbtc; } +if (what == BaseURL3) // Denarius News Feed +{ + + // QNetworkReply is a QIODevice. So we read from it just like it was a file + QString dnewsfeed = finished->readAll(); + //dnewsfeedx = (dnewsfeed.toDouble()); + //dnewsfeed = QString::number(dnewsfeedx, 'f', 8); + + dnrnewsfeed = dnewsfeed; +} finished->deleteLater(); } @@ -232,6 +244,10 @@ void OverviewPage::setBalance(qint64 balance, qint64 lockedbalance, qint64 stake ui->labelTradeLink->setTextFormat(Qt::RichText); ui->labelTradeLink->setTextInteractionFlags(Qt::TextBrowserInteraction); ui->labelTradeLink->setOpenExternalLinks(true); + + QString news; + news = dnrnewsfeed; + ui->labelNewsFeed->setText(news); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp new file mode 100644 index 00000000..a84ec3cb --- /dev/null +++ b/src/qt/peertablemodel.cpp @@ -0,0 +1,242 @@ +// Copyright (c) 2011-2013 The Bitcoin Core developers +// Copyright (c) 2018 The Denarius Developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "peertablemodel.h" + +#include "clientmodel.h" +#include "guiconstants.h" +#include "guiutil.h" + +#include "net.h" +#include "sync.h" + +#include +#include +#include + +bool NodeLessThan::operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const +{ + const CNodeStats *pLeft = &(left.nodeStats); + const CNodeStats *pRight = &(right.nodeStats); + + if (order == Qt::DescendingOrder) + std::swap(pLeft, pRight); + + switch(column) + { + case PeerTableModel::Address: + return pLeft->addrName.compare(pRight->addrName) < 0; + case PeerTableModel::Subversion: + return pLeft->strSubVer.compare(pRight->strSubVer) < 0; + case PeerTableModel::Ping: + return pLeft->dPingTime < pRight->dPingTime; + } + + return false; +} + +// private implementation +class PeerTablePriv +{ +public: + /** Local cache of peer information */ + QList cachedNodeStats; + /** Column to sort nodes by */ + int sortColumn; + /** Order (ascending or descending) to sort nodes by */ + Qt::SortOrder sortOrder; + /** Index of rows by node ID */ + std::map mapNodeRows; + + /** Pull a full list of peers from vNodes into our cache */ + void refreshPeers() + { + { + TRY_LOCK(cs_vNodes, lockNodes); + if (!lockNodes) + { + // skip the refresh if we can't immediately get the lock + return; + } + cachedNodeStats.clear(); +#if QT_VERSION >= 0x040700 + cachedNodeStats.reserve(vNodes.size()); +#endif + BOOST_FOREACH(CNode* pnode, vNodes) + { + CNodeCombinedStats stats; + stats.nodeStateStats.nMisbehavior = 0; + stats.nodeStateStats.nSyncHeight = -1; + stats.fNodeStateStatsAvailable = false; + pnode->copyStats(stats.nodeStats); + cachedNodeStats.append(stats); + } + } + + // Try to retrieve the CNodeStateStats for each node. + { + TRY_LOCK(cs_main, lockMain); + if (lockMain) + { + BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats) + stats.fNodeStateStatsAvailable = GetNodeStateStats(stats.nodeStats.nodeid, stats.nodeStateStats); + } + } + + if (sortColumn >= 0) + // sort cacheNodeStats (use stable sort to prevent rows jumping around unneceesarily) + qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder)); + + // build index map + mapNodeRows.clear(); + int row = 0; + BOOST_FOREACH(CNodeCombinedStats &stats, cachedNodeStats) + mapNodeRows.insert(std::pair(stats.nodeStats.nodeid, row++)); + } + + int size() + { + return cachedNodeStats.size(); + } + + CNodeCombinedStats *index(int idx) + { + if(idx >= 0 && idx < cachedNodeStats.size()) { + return &cachedNodeStats[idx]; + } else { + return 0; + } + } +}; + +PeerTableModel::PeerTableModel(ClientModel *parent) : + QAbstractTableModel(parent), + clientModel(parent), + timer(0) +{ + columns << tr("Address/Hostname") << tr("User Agent") << tr("Ping Time"); + priv = new PeerTablePriv(); + // default to unsorted + priv->sortColumn = -1; + + // set up timer for auto refresh + timer = new QTimer(); + connect(timer, SIGNAL(timeout()), SLOT(refresh())); + timer->setInterval(MODEL_UPDATE_DELAY); + + // load initial data + refresh(); +} + +void PeerTableModel::startAutoRefresh() +{ + timer->start(); +} + +void PeerTableModel::stopAutoRefresh() +{ + timer->stop(); +} + +int PeerTableModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return priv->size(); +} + +int PeerTableModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return columns.length();; +} + +QVariant PeerTableModel::data(const QModelIndex &index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + CNodeCombinedStats *rec = static_cast(index.internalPointer()); + + if (role == Qt::DisplayRole) { + switch(index.column()) + { + case Address: + return QString::fromStdString(rec->nodeStats.addrName); + case Subversion: + return QString::fromStdString(rec->nodeStats.strSubVer); + case Ping: + return GUIUtil::formatPingTime(rec->nodeStats.dPingTime); + } + } else if (role == Qt::TextAlignmentRole) { + if (index.column() == Ping) + return (int)(Qt::AlignRight | Qt::AlignVCenter); + } + + return QVariant(); +} + +QVariant PeerTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if(orientation == Qt::Horizontal) + { + if(role == Qt::DisplayRole && section < columns.size()) + { + return columns[section]; + } + } + return QVariant(); +} + +Qt::ItemFlags PeerTableModel::flags(const QModelIndex &index) const +{ + if(!index.isValid()) + return 0; + + Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + return retval; +} + +QModelIndex PeerTableModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + CNodeCombinedStats *data = priv->index(row); + + if (data) + { + return createIndex(row, column, data); + } + else + { + return QModelIndex(); + } +} + +const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) +{ + return priv->index(idx); +} + +void PeerTableModel::refresh() +{ + emit layoutAboutToBeChanged(); + priv->refreshPeers(); + emit layoutChanged(); +} + +int PeerTableModel::getRowByNodeId(NodeId nodeid) +{ + std::map::iterator it = priv->mapNodeRows.find(nodeid); + if (it == priv->mapNodeRows.end()) + return -1; + + return it->second; +} + +void PeerTableModel::sort(int column, Qt::SortOrder order) +{ + priv->sortColumn = column; + priv->sortOrder = order; + refresh(); +} diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h new file mode 100644 index 00000000..7c2f5748 --- /dev/null +++ b/src/qt/peertablemodel.h @@ -0,0 +1,81 @@ +// Copyright (c) 2011-2013 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef DENARIUS_QT_PEERTABLEMODEL_H +#define DENARIUS_QT_PEERTABLEMODEL_H + +#include "main.h" +#include "net.h" + +#include +#include + +class ClientModel; +class PeerTablePriv; + +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + +struct CNodeCombinedStats { + CNodeStats nodeStats; + CNodeStateStats nodeStateStats; + bool fNodeStateStatsAvailable; +}; + +class NodeLessThan +{ +public: + NodeLessThan(int nColumn, Qt::SortOrder fOrder) : + column(nColumn), order(fOrder) {} + bool operator()(const CNodeCombinedStats &left, const CNodeCombinedStats &right) const; + +private: + int column; + Qt::SortOrder order; +}; + +/** + Qt model providing information about connected peers, similar to the + "getpeerinfo" RPC call. Used by the rpc console UI. + */ +class PeerTableModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit PeerTableModel(ClientModel *parent = 0); + const CNodeCombinedStats *getNodeStats(int idx); + int getRowByNodeId(NodeId nodeid); + void startAutoRefresh(); + void stopAutoRefresh(); + + enum ColumnIndex { + Address = 0, + Subversion = 1, + Ping = 2 + }; + + /** @name Methods overridden from QAbstractTableModel + @{*/ + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + QModelIndex index(int row, int column, const QModelIndex &parent) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + void sort(int column, Qt::SortOrder order); + /*@}*/ + +public slots: + void refresh(); + +private: + ClientModel *clientModel; + QStringList columns; + PeerTablePriv *priv; + QTimer *timer; +}; + +#endif // BITCOIN_QT_PEERTABLEMODEL_H diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 2439c86a..3a982b47 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -4,6 +4,7 @@ #include "clientmodel.h" #include "bitcoinrpc.h" #include "guiutil.h" +#include "peertablemodel.h" #include #include @@ -190,7 +191,8 @@ void RPCExecutor::request(const QString &command) RPCConsole::RPCConsole(QWidget *parent) : QDialog(parent), ui(new Ui::RPCConsole), - historyPtr(0) + historyPtr(0), + cachedNodeid(-1) { ui->setupUi(this); @@ -212,6 +214,9 @@ RPCConsole::RPCConsole(QWidget *parent) : startExecutor(); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); + ui->detailWidget->hide(); + ui->peerHeading->setText(tr("Select a peer to view detailed information.")); + clear(); } @@ -270,6 +275,19 @@ void RPCConsole::setClientModel(ClientModel *model) updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); + // set up peer table + ui->peerWidget->setModel(model->getPeerTableModel()); + ui->peerWidget->verticalHeader()->hide(); + ui->peerWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); + ui->peerWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->peerWidget->setSelectionMode(QAbstractItemView::SingleSelection); + ui->peerWidget->setColumnWidth(PeerTableModel::Address, ADDRESS_COLUMN_WIDTH); + ui->peerWidget->setColumnWidth(PeerTableModel::Subversion, SUBVERSION_COLUMN_WIDTH); + ui->peerWidget->setColumnWidth(PeerTableModel::Ping, PING_COLUMN_WIDTH); + + // connect the peerWidget selection model to our peerSelected() handler + connect(ui->peerWidget->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(peerSelected(const QItemSelection &, const QItemSelection &))); + connect(model->getPeerTableModel(), SIGNAL(layoutChanged()), this, SLOT(peerLayoutChanged())); // Provide initial values ui->clientVersion->setText(model->formatFullVersion()); @@ -277,6 +295,8 @@ void RPCConsole::setClientModel(ClientModel *model) ui->buildDate->setText(model->formatBuildDate()); ui->startupTime->setText(model->formatClientStartupTime()); + //ui->networkName->setText(QString::fromStdString(Params().NetworkIDString())); + setNumConnections(model->getNumConnections()); ui->isTestNet->setChecked(model->isTestNet()); ui->isNativeTor->setChecked(model->isNativeTor()); @@ -486,6 +506,148 @@ void RPCConsole::updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut) ui->lblBytesOut->setText(FormatBytes(totalBytesOut)); } +void RPCConsole::peerSelected(const QItemSelection &selected, const QItemSelection &deselected) +{ + Q_UNUSED(deselected); + + if (!clientModel || selected.indexes().isEmpty()) + return; + + const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(selected.indexes().first().row()); + if (stats) + updateNodeDetail(stats); +} + +void RPCConsole::peerLayoutChanged() +{ + if (!clientModel) + return; + + const CNodeCombinedStats *stats = NULL; + bool fUnselect = false; + bool fReselect = false; + + if (cachedNodeid == -1) // no node selected yet + return; + + // find the currently selected row + int selectedRow; + QModelIndexList selectedModelIndex = ui->peerWidget->selectionModel()->selectedIndexes(); + if (selectedModelIndex.isEmpty()) + selectedRow = -1; + else + selectedRow = selectedModelIndex.first().row(); + + // check if our detail node has a row in the table (it may not necessarily + // be at selectedRow since its position can change after a layout change) + int detailNodeRow = clientModel->getPeerTableModel()->getRowByNodeId(cachedNodeid); + + if (detailNodeRow < 0) + { + // detail node dissapeared from table (node disconnected) + fUnselect = true; + cachedNodeid = -1; + ui->detailWidget->hide(); + ui->peerHeading->setText(tr("Select a peer to view detailed information.")); + } + else + { + if (detailNodeRow != selectedRow) + { + // detail node moved position + fUnselect = true; + fReselect = true; + } + + // get fresh stats on the detail node. + stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); + } + + if (fUnselect && selectedRow >= 0) + { + ui->peerWidget->selectionModel()->select(QItemSelection(selectedModelIndex.first(), selectedModelIndex.last()), + QItemSelectionModel::Deselect); + } + + if (fReselect) + { + ui->peerWidget->selectRow(detailNodeRow); + } + + if (stats) + updateNodeDetail(stats); +} + +void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats) +{ + // Update cached nodeid + cachedNodeid = stats->nodeStats.nodeid; + + // update the detail ui with latest node information + QString peerAddrDetails(QString::fromStdString(stats->nodeStats.addrName)); + if (!stats->nodeStats.addrLocal.empty()) + peerAddrDetails += "
" + tr("via %1").arg(QString::fromStdString(stats->nodeStats.addrLocal)); + ui->peerHeading->setText(peerAddrDetails); + ui->peerServices->setText(GUIUtil::formatServicesStr(stats->nodeStats.nServices)); + ui->peerLastSend->setText(stats->nodeStats.nLastSend ? GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nLastSend) : tr("never")); + ui->peerLastRecv->setText(stats->nodeStats.nLastRecv ? GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nLastRecv) : tr("never")); + ui->peerBytesSent->setText(FormatBytes(stats->nodeStats.nSendBytes)); + ui->peerBytesRecv->setText(FormatBytes(stats->nodeStats.nRecvBytes)); + ui->peerConnTime->setText(GUIUtil::formatDurationStr(GetTime() - stats->nodeStats.nTimeConnected)); + ui->peerPingTime->setText(GUIUtil::formatPingTime(stats->nodeStats.dPingTime)); + ui->timeoffset->setText(GUIUtil::formatTimeOffset(stats->nodeStats.nTimeOffset)); + ui->peerVersion->setText(QString("%1").arg(stats->nodeStats.nVersion)); + ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.strSubVer)); + ui->peerId->setText(QString("%1").arg(stats->nodeStats.nodeid)); + ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound")); + ui->peerHeight->setText(QString("%1").arg(stats->nodeStats.nStartingHeight)); + + // This check fails for example if the lock was busy and + // nodeStateStats couldn't be fetched. + if (stats->fNodeStateStatsAvailable) { + // Ban score is init to 0 + ui->peerBanScore->setText(QString("%1").arg(stats->nodeStateStats.nMisbehavior)); + + // Sync height is init to -1 + if (stats->nodeStateStats.nSyncHeight > -1) + ui->peerSyncHeight->setText(QString("%1").arg(stats->nodeStateStats.nSyncHeight)); + else + ui->peerSyncHeight->setText(tr("Unknown")); + } else { + ui->peerBanScore->setText(tr("Fetching...")); + ui->peerSyncHeight->setText(tr("Fetching...")); + } + + ui->detailWidget->show(); +} + +void RPCConsole::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); +} + +void RPCConsole::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + + if (!clientModel) + return; + + // start PeerTableModel auto refresh + clientModel->getPeerTableModel()->startAutoRefresh(); +} + +void RPCConsole::hideEvent(QHideEvent *event) +{ + QWidget::hideEvent(event); + + if (!clientModel) + return; + + // stop PeerTableModel auto refresh + clientModel->getPeerTableModel()->stopAutoRefresh(); +} + void RPCConsole::on_showCLOptionsButton_clicked() { GUIUtil::HelpMessageBox help; diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 70a71138..b665f4db 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -1,12 +1,22 @@ -#ifndef RPCCONSOLE_H -#define RPCCONSOLE_H +#ifndef DENARIUS_QT_RPCCONSOLE_H +#define DENARIUS_QT_RPCCONSOLE_H + +#include "guiutil.h" +#include "peertablemodel.h" + +#include "net.h" #include +class ClientModel; + namespace Ui { class RPCConsole; } -class ClientModel; + +QT_BEGIN_NAMESPACE +class QItemSelection; +QT_END_NAMESPACE /** Local Bitcoin RPC console. */ class RPCConsole: public QDialog @@ -26,11 +36,12 @@ class RPCConsole: public QDialog CMD_REPLY, CMD_ERROR }; - + enum TabTypes { TAB_INFO = 0, TAB_CONSOLE = 1, - TAB_GRAPH = 2 + TAB_GRAPH = 2, + TAB_PEER = 3 }; protected: @@ -43,11 +54,14 @@ private slots: void on_openDebugLogfileButton_clicked(); /** display messagebox with program parameters (same as bitcoin-qt --help) */ void on_showCLOptionsButton_clicked(); - + /** change the time range of the network traffic graph */ void on_sldGraphRange_valueChanged(int value); /** update traffic statistics */ void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut); + void resizeEvent(QResizeEvent *event); + void showEvent(QShowEvent *event); + void hideEvent(QHideEvent *event); public slots: void clear(); @@ -60,7 +74,11 @@ public slots: void browseHistory(int offset); /** Scroll console view to end */ void scrollToEnd(); - + /** Handle selection of peer in peers list */ + void peerSelected(const QItemSelection &selected, const QItemSelection &deselected); + /** Handle updated peer information */ + void peerLayoutChanged(); + /** set which tab has the focus (is visible) */ void setTabFocus(enum TabTypes tabType); signals: @@ -71,11 +89,21 @@ public slots: private: static QString FormatBytes(quint64 bytes); void setTrafficGraphRange(int mins); + /** show detailed information on ui about selected node */ + void updateNodeDetail(const CNodeCombinedStats *stats); + + enum ColumnWidths + { + ADDRESS_COLUMN_WIDTH = 200, + SUBVERSION_COLUMN_WIDTH = 100, + PING_COLUMN_WIDTH = 80 + }; + Ui::RPCConsole *ui; ClientModel *clientModel; QStringList history; int historyPtr; - + NodeId cachedNodeid; void startExecutor(); }; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 05dd3131..bd608d52 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -86,7 +86,7 @@ class TransactionTablePriv */ void updateWallet(const uint256 &hash, int status) { - OutputDebugStringF("updateWallet %s %i\n", hash.ToString().c_str(), status); + if (fDebugChain && fDebugNet) OutputDebugStringF("updateWallet %s %i\n", hash.ToString().c_str(), status); { LOCK2(cs_main, wallet->cs_wallet); @@ -114,7 +114,7 @@ class TransactionTablePriv status = CT_DELETED; /* In model, but want to hide, treat as deleted */ } - OutputDebugStringF(" inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n", + if (fDebugChain && fDebugNet) OutputDebugStringF(" inWallet=%i inModel=%i Index=%i-%i showTransaction=%i derivedStatus=%i\n", inWallet, inModel, lowerIndex, upperIndex, showTransaction, status); switch(status) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 36151de8..d5bc2b6c 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -127,15 +127,17 @@ void WalletModel::pollBalanceChanged() if(!lockWallet) return; - if(nBestHeight != cachedNumBlocks) + if(fForceBalanceCheck && nBestHeight != cachedNumBlocks) { // Balance and number of transactions might have changed cachedNumBlocks = nBestHeight; checkBalanceChanged(); - if(transactionTableModel) - transactionTableModel->updateConfirmations(); + //if(transactionTableModel) + //transactionTableModel->updateConfirmations(); } + + fForceBalanceCheck = false; } void WalletModel::checkBalanceChanged() @@ -173,8 +175,9 @@ void WalletModel::updateTransaction(const QString &hash, int status) transactionTableModel->updateTransaction(hash, status); // Balance and number of transactions might have changed - checkBalanceChanged(); + // checkBalanceChanged(); + fForceBalanceCheck = true; int newNumTransactions = getNumTransactions(); if(cachedNumTransactions != newNumTransactions) { diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 3983b95b..dc8052e5 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -172,6 +172,7 @@ class WalletModel : public QObject int cachedTxLocks; EncryptionStatus cachedEncryptionStatus; int cachedNumBlocks; + bool fForceBalanceCheck; QTimer *pollTimer; diff --git a/src/rpcfortuna.cpp b/src/rpcfortuna.cpp index 7edca9a3..937f0f68 100644 --- a/src/rpcfortuna.cpp +++ b/src/rpcfortuna.cpp @@ -230,7 +230,7 @@ Value fortunastake(const Array& params, bool fHelp) mn.Check(); if(strCommand == "active"){ - obj.push_back(Pair(mn.addr.ToString().c_str(), (int)mn.IsActive(pindexBest))); + obj.push_back(Pair(mn.addr.ToString().c_str(), (int)mn.IsActive())); } else if (strCommand == "txid") { obj.push_back(Pair(mn.addr.ToString().c_str(), mn.vin.prevout.hash.ToString().c_str())); } else if (strCommand == "pubkey") { @@ -262,7 +262,7 @@ Value fortunastake(const Array& params, bool fHelp) } else if (strCommand == "full") { Object list; - list.push_back(Pair("active", (int)mn.IsActive(pindexBest))); + list.push_back(Pair("active", (int)mn.IsActive())); list.push_back(Pair("txid", mn.vin.prevout.hash.ToString().c_str())); list.push_back(Pair("n", (int64_t)mn.vin.prevout.n)); @@ -584,8 +584,8 @@ Value fortunastake(const Array& params, bool fHelp) address = address2.ToString(); localObj.push_back(Pair("payment_address", address)); //localObj.push_back(Pair("rank", GetFortunastakeRank(mn, pindexBest))); - localObj.push_back(Pair("network_status", mn.IsActive(pindexBest) ? "active" : "registered")); - if (mn.IsActive(pindexBest)) { + localObj.push_back(Pair("network_status", mn.IsActive() ? "active" : "registered")); + if (mn.IsActive()) { localObj.push_back(Pair("activetime",(mn.lastTimeSeen - mn.now))); } diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 5691c1c0..8a5f9efb 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -533,7 +533,7 @@ Value getblocktemplate(const Array& params, bool fHelp) } else { if(pindexPrev->nHeight+1 >= BLOCK_START_FORTUNASTAKE_PAYMENTS) bFortunastakePayments = true; } - if(fDebug) { printf("GetBlockTemplate(): Fortunastake Payments : %i\n", bFortunastakePayments); } + if(fDebug && fDebugFS) { printf("GetBlockTemplate(): Fortunastake Payments : %i\n", bFortunastakePayments); } if(!fortunastakePayments.GetBlockPayee(pindexPrev->nHeight+1, payee)){ //no fortunastake detected @@ -563,7 +563,7 @@ Value getblocktemplate(const Array& params, bool fHelp) payee = GetScriptForDestination(burnAddr.Get()); } } - printf("getblock : payee = %i, bFortunastake = %i\n",payee != CScript(),bFortunastakePayments); + if (fDebug && fDebugNet) printf("getblock : payee = %i, bFortunastake = %i\n",payee != CScript(),bFortunastakePayments); if(payee != CScript()){ CTxDestination address1; ExtractDestination(payee, address1); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 7f4fc957..c40432a4 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -607,6 +607,7 @@ Value fetchbalance(const Array& params, bool fHelp) "Returns an object containing various wallet balance info."); Object obj, watchonly; obj.push_back(Pair("totalbalance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("anonbalance", ValueFromAmount(pwalletMain->GetAnonBalance()))); obj.push_back(Pair("locked", ValueFromAmount(pwalletMain->GetLockedBalance()))); obj.push_back(Pair("unlocked", ValueFromAmount(pwalletMain->GetUnlockedBalance()))); obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint()))); diff --git a/src/util.cpp b/src/util.cpp index 6c777ce7..6301f20c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -81,6 +81,7 @@ bool fDebugFS = false; bool fDebugChain = false; bool fDebugRingSig = false; bool fNoSmsg = false; +bool fDisableStealth = false; bool fPrintToConsole = false; bool fPrintToDebugger = false; bool fRequestShutdown = false; diff --git a/src/util.h b/src/util.h index 0999c807..ae491c4b 100644 --- a/src/util.h +++ b/src/util.h @@ -150,6 +150,7 @@ extern bool fDebugFS; extern bool fDebugChain; extern bool fDebugRingSig; extern bool fNoSmsg; +extern bool fDisableStealth; extern bool fPrintToConsole; extern bool fPrintToDebugger; extern bool fRequestShutdown; diff --git a/src/version.h b/src/version.h index e3026745..0eb274c0 100644 --- a/src/version.h +++ b/src/version.h @@ -30,7 +30,7 @@ static const int DATABASE_VERSION = 21212; // network protocol versioning // -static const int PROTOCOL_VERSION = 31005; //Protocol is now 31005 as of D v3.3 +static const int PROTOCOL_VERSION = 33500; //Protocol is now 33500 as of D v3.3.5 // intial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 21212; @@ -43,14 +43,14 @@ extern int MIN_MN_PROTO_VERSION; // nTime field added to CAddress, starting with this version; // if possible, avoid requesting addresses nodes older than this -static const int CADDR_TIME_VERSION = 31402; +static const int CADDR_TIME_VERSION = 33500; // start sharing node timeinfo with this proto version 33500 // only request blocks from nodes outside this range of versions static const int NOBLKS_VERSION_START = 70002; static const int NOBLKS_VERSION_END = 70006; // BIP 0031, pong message, is enabled for all versions AFTER this one -static const int BIP0031_VERSION = 60000; +static const int BIP0031_VERSION = 21212; // changed to 21212 from 60000 - start sending nonces to all clients. // "mempool" command, enhanced "getdata" behavior starts with this version: static const int MEMPOOL_GD_VERSION = 60002; diff --git a/src/wallet.cpp b/src/wallet.cpp index 4d659190..b9acd4bd 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -775,7 +775,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl }; mapValue_t mapNarr; - FindStealthTransactions(tx, mapNarr); + if (stealthAddresses.size() > 0 && !fDisableStealth) FindStealthTransactions(tx, mapNarr); bool fIsMine = false; if (tx.nVersion == ANON_TXN_VERSION) @@ -1232,9 +1232,32 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) CBlockIndex* pindex = pindexStart; { - LOCK2(cs_main, cs_wallet); - while (pindex) + + int dProgressTop; + { + LOCK(cs_main); + dProgressTop = pindexBest->nHeight; + } + + int dProgressStart = pindex ? pindex->nHeight : 0; + int dProgressCurrent = dProgressStart; + int dProgressTotal = dProgressTop - dProgressStart; + double dProgressShow = 0; + double dProgressShowPrev = 0; + + while (pindex && !fShutdown) { + if (dProgressCurrent > 0) + dProgressShow = ((static_cast(dProgressCurrent) / dProgressTop) * 100.0); + + if ((pindex->nHeight % 100 == 0) && (dProgressTotal > 0)) + { + if (dProgressShowPrev != dProgressShow) + { + dProgressShowPrev = dProgressShow; + uiInterface.InitMessage(strprintf("%s %d/%d %s... (%.2f%%)",_("Rescanning").c_str(), dProgressCurrent , dProgressTop,_("blocks").c_str(),dProgressShow)); + } + } // no need to read and scan block, if block was created before // our wallet birthday (as adjusted for block time variability) if (nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) { @@ -1246,11 +1269,18 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) block.ReadFromDisk(pindex, true); BOOST_FOREACH(CTransaction& tx, block.vtx) { + LOCK(cs_wallet); if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) ret++; } pindex = pindex->pnext; + + // Update current height for progress + if (pindex) dProgressCurrent = pindex->nHeight; + } + + uiInterface.InitMessage(_("Rescanning complete.")); } return ret; } @@ -1561,6 +1591,7 @@ int64_t CWallet::GetImmatureWatchOnlyBalance() const } return nTotal; } + // populate vCoins with vector of spendable COutputs void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { @@ -3078,8 +3109,8 @@ bool CWallet::SendStealthMoneyToDestination(CStealthAddress& sxAddress, int64_t bool CWallet::FindStealthTransactions(const CTransaction& tx, mapValue_t& mapNarr) { - if (fDebug) - printf("FindStealthTransactions() tx: %s\n", tx.GetHash().GetHex().c_str()); + //if (fDebug) + //printf("FindStealthTransactions() tx: %s\n", tx.GetHash().GetHex().c_str()); mapNarr.clear(); @@ -3403,7 +3434,11 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int // Select coins with suitable depth if (!SelectCoinsForStaking(nBalance - nReserveBalance, txNew.nTime, setCoins, nValueIn)) + { + if (fDebug && GetBoolArg("-printcoinstakedebug")) + printf("CreateCoinStake() : valid staking coins not found\n"); return false; + } if (setCoins.empty()) return false; @@ -3428,13 +3463,15 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int continue; } - static int nMaxStakeSearchInterval = 60; + static int nMaxStakeSearchInterval = 30; if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval) continue; // only count coins meeting min age requirement bool fKernelFound = false; for (unsigned int n=0; n nBalance - nReserveBalance) return false; @@ -3680,7 +3719,7 @@ bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) }; mapValue_t mapNarr; - FindStealthTransactions(wtxNew, mapNarr); + if (stealthAddresses.size() > 0 && !fDisableStealth) FindStealthTransactions(wtxNew, mapNarr); bool fIsMine = false; if (wtxNew.nVersion == ANON_TXN_VERSION) diff --git a/src/wallet.h b/src/wallet.h index 60b7a694..fa3cde3f 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -1131,6 +1131,12 @@ class CWalletTx : public CMerkleTx // Quick answer in most cases if (!IsFinal()) return false; + // Coins newer than our current chain can't be trusted + if (nTime > pindexBest->GetBlockTime()) + return false; + // Coins whose block is not in our chain can't be trusted + if (!mapBlockIndex.count(hashBlock)) + return false; int nDepth = GetDepthInMainChain(); if (nDepth >= 1) return true;