Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
da38e2b
Change litemode from disabling all Dash specific features to disablin…
PastaPastaPasta Jun 13, 2020
0297eb4
Change litemode to disablegovernance (#3577)
PastaPastaPasta Jun 28, 2020
baf18a3
More pruning improvements (#3579)
PastaPastaPasta Jul 8, 2020
bac02d0
More accurate fee calculation in CreateDenominated (#3588)
UdjinM6 Jul 7, 2020
b45b137
Prefer creating larger denoms at the second step of CreateDenominated…
UdjinM6 Jul 8, 2020
6a249f5
Merge #13564: [wallet] loadwallet shouldn't create new wallets. (#3592)
PastaPastaPasta Jul 8, 2020
aa3bec6
evo: Avoid some unnecessary copying in BuildNewListFromBlock (#3594)
xdustinface Jul 10, 2020
62c3fb5
llmq: Fix thread handling in CDKGSessionManager and CDKGSessionHandle…
xdustinface Jul 16, 2020
a170c64
Fail GetTransaction when the block from txindex is not in mapBlockInd…
codablock Jul 14, 2020
f425316
util: Change TraceThread's "name" type: "const char*" -> "const std::…
xdustinface Jul 14, 2020
508fc2d
privatesend: Increase max participants to 20 (#3610)
PastaPastaPasta Jul 17, 2020
8d99e78
Fix `-resetguisettings` (#3624)
UdjinM6 Jul 27, 2020
bbb0064
init: Fix crash due to -litemode and improve its deprecation warning …
xdustinface Jul 27, 2020
4db8968
rpc: update help text for BLS operator key arguments (#3628)
CryptoTeller Jul 28, 2020
abe3d57
Include protocol version into MNAUTH (#3631)
UdjinM6 Aug 4, 2020
c232f2a
[RPC] Show address of fundDest when no funds (#3649)
akshaynexus Aug 9, 2020
4414b5c
llmq: Fix spork check in CSigSharesManager::ForceReAnnouncement (#3650)
xdustinface Aug 9, 2020
bd5c047
Implement a safer version of GetCrashInfoFromException (#3652)
UdjinM6 Aug 14, 2020
8aeeb97
Print exception origin in crash messages (#3653)
UdjinM6 Aug 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,9 @@ class CMainParams : public CChainParams {
nLLMQConnectionRetryTimeout = 60;

nPoolMinParticipants = 3;
nPoolMaxParticipants = 5; // TODO: bump on next HF / mandatory upgrade
nPoolNewMinParticipants = 3;
nPoolMaxParticipants = 5;
nPoolNewMaxParticipants = 20;
nFulfilledRequestExpireTime = 60*60; // fulfilled requests expire in 1 hour

vSporkAddresses = {"Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh"};
Expand Down Expand Up @@ -582,8 +584,10 @@ class CTestNetParams : public CChainParams {
fAllowMultiplePorts = true;
nLLMQConnectionRetryTimeout = 60;

nPoolMinParticipants = 3; // TODO drop to 2 with next mandatory upgrade
nPoolMinParticipants = 3;
nPoolNewMinParticipants = 2;
nPoolMaxParticipants = 5;
nPoolNewMaxParticipants = 20;
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes

vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"};
Expand Down Expand Up @@ -740,8 +744,10 @@ class CDevNetParams : public CChainParams {
fAllowMultiplePorts = true;
nLLMQConnectionRetryTimeout = 60;

nPoolMinParticipants = 3; // same, drop to 2 w/ breaking change
nPoolMinParticipants = 3;
nPoolNewMinParticipants = 2;
nPoolMaxParticipants = 5;
nPoolNewMaxParticipants = 20;
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes

vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"};
Expand Down Expand Up @@ -853,7 +859,9 @@ class CRegTestParams : public CChainParams {

nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes
nPoolMinParticipants = 2;
nPoolNewMinParticipants = 2;
nPoolMaxParticipants = 5;
nPoolNewMaxParticipants = 20;

// privKey: cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK
vSporkAddresses = {"yj949n1UH6fDhw6HtVE5VMj2iSTaSWBMcW"};
Expand Down
4 changes: 4 additions & 0 deletions src/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ class CChainParams
void UpdateLLMQTestParams(int size, int threshold);
void UpdateLLMQDevnetParams(int size, int threshold);
int PoolMinParticipants() const { return nPoolMinParticipants; }
int PoolNewMinParticipants() const { return nPoolNewMinParticipants; }
int PoolMaxParticipants() const { return nPoolMaxParticipants; }
int PoolNewMaxParticipants() const { return nPoolNewMaxParticipants; }
int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; }
const std::vector<std::string>& SporkAddresses() const { return vSporkAddresses; }
int MinSporkKeys() const { return nMinSporkKeys; }
Expand Down Expand Up @@ -118,7 +120,9 @@ class CChainParams
CCheckpointData checkpointData;
ChainTxData chainTxData;
int nPoolMinParticipants;
int nPoolNewMinParticipants;
int nPoolMaxParticipants;
int nPoolNewMaxParticipants;
int nFulfilledRequestExpireTime;
std::vector<std::string> vSporkAddresses;
int nMinSporkKeys;
Expand Down
6 changes: 2 additions & 4 deletions src/dsnotificationinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,13 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
privateSendClient.UpdatedBlockTip(pindexNew);
#endif // ENABLE_WALLET

if (fLiteMode)
return;

llmq::quorumInstantSendManager->UpdatedBlockTip(pindexNew);
llmq::chainLocksHandler->UpdatedBlockTip(pindexNew);

governance.UpdatedBlockTip(pindexNew, connman);
llmq::quorumManager->UpdatedBlockTip(pindexNew, fInitialDownload);
llmq::quorumDKGSessionManager->UpdatedBlockTip(pindexNew, fInitialDownload);

if (!fDisableGovernance) governance.UpdatedBlockTip(pindexNew, connman);
}

void CDSNotificationInterface::TransactionAddedToMempool(const CTransactionRef& ptx, int64_t nAcceptTime)
Expand Down
17 changes: 7 additions & 10 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,9 +670,9 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
// has been reached, but the block hash will then point to the block at nMasternodeMinimumConfirmations
int nConfirmations = pindexPrev->nHeight - dmn->pdmnState->nRegisteredHeight;
if (nConfirmations >= Params().GetConsensus().nMasternodeMinimumConfirmations) {
CDeterministicMNState newState = *dmn->pdmnState;
newState.UpdateConfirmedHash(dmn->proTxHash, pindexPrev->GetBlockHash());
newList.UpdateMN(dmn->proTxHash, std::make_shared<CDeterministicMNState>(newState));
auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
newState->UpdateConfirmedHash(dmn->proTxHash, pindexPrev->GetBlockHash());
newList.UpdateMN(dmn->proTxHash, newState);
}
});

Expand Down Expand Up @@ -730,17 +730,14 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C
}

dmn->nOperatorReward = proTx.nOperatorReward;
dmn->pdmnState = std::make_shared<CDeterministicMNState>(proTx);

CDeterministicMNState dmnState = *dmn->pdmnState;
dmnState.nRegisteredHeight = nHeight;

auto dmnState = std::make_shared<CDeterministicMNState>(proTx);
dmnState->nRegisteredHeight = nHeight;
if (proTx.addr == CService()) {
// start in banned pdmnState as we need to wait for a ProUpServTx
dmnState.nPoSeBanHeight = nHeight;
dmnState->nPoSeBanHeight = nHeight;
}

dmn->pdmnState = std::make_shared<CDeterministicMNState>(dmnState);
dmn->pdmnState = dmnState;

newList.AddMN(dmn);

Expand Down
21 changes: 19 additions & 2 deletions src/evo/mnauth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,15 @@ void CMNAuth::PushMNAUTH(CNode* pnode, CConnman& connman)
// It does not protect against:
// node1 -> Eve -> node2
// This is ok as we only use MNAUTH as a DoS protection and not for sensitive stuff
signHash = ::SerializeHash(std::make_tuple(*activeMasternodeInfo.blsPubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound));
int nOurNodeVersion{PROTOCOL_VERSION};
if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
nOurNodeVersion = gArgs.GetArg("-pushversion", PROTOCOL_VERSION);
}
if (pnode->nVersion < MNAUTH_NODE_VER_VERSION || nOurNodeVersion < MNAUTH_NODE_VER_VERSION) {
signHash = ::SerializeHash(std::make_tuple(*activeMasternodeInfo.blsPubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound));
} else {
signHash = ::SerializeHash(std::make_tuple(*activeMasternodeInfo.blsPubKeyOperator, pnode->receivedMNAuthChallenge, pnode->fInbound, nOurNodeVersion));
}
}

CMNAuth mnauth;
Expand Down Expand Up @@ -102,8 +110,17 @@ void CMNAuth::ProcessMessage(CNode* pnode, const std::string& strCommand, CDataS
uint256 signHash;
{
LOCK(pnode->cs_mnauth);
int nOurNodeVersion{PROTOCOL_VERSION};
if (Params().NetworkIDString() != CBaseChainParams::MAIN && gArgs.IsArgSet("-pushversion")) {
nOurNodeVersion = gArgs.GetArg("-pushversion", PROTOCOL_VERSION);
}
// See comment in PushMNAUTH (fInbound is negated here as we're on the other side of the connection)
signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound));
if (pnode->nVersion < MNAUTH_NODE_VER_VERSION || nOurNodeVersion < MNAUTH_NODE_VER_VERSION) {
signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound));
} else {
signHash = ::SerializeHash(std::make_tuple(dmn->pdmnState->pubKeyOperator, pnode->sentMNAuthChallenge, !pnode->fInbound, pnode->nVersion.load()));
}
LogPrint(BCLog::NET_NETCONN, "CMNAuth::%s -- constructed signHash for nVersion %d, peer=%d\n", __func__, pnode->nVersion, pnode->GetId());
}

if (!mnauth.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator.Get(), signHash)) {
Expand Down
5 changes: 2 additions & 3 deletions src/governance/governance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ bool CGovernanceManager::SerializeVoteForHash(const uint256& nHash, CDataStream&

void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
{
// lite mode is not supported
if (fLiteMode) return;
if (fDisableGovernance) return;
if (!masternodeSync.IsBlockchainSynced()) return;

// ANOTHER USER IS ASKING US TO HELP THEM SYNC GOVERNANCE OBJECT DATA
Expand Down Expand Up @@ -547,7 +546,7 @@ struct sortProposalsByVotes {

void CGovernanceManager::DoMaintenance(CConnman& connman)
{
if (fLiteMode || !masternodeSync.IsSynced() || ShutdownRequested()) return;
if (fDisableGovernance || !masternodeSync.IsSynced() || ShutdownRequested()) return;

// CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES

Expand Down
85 changes: 53 additions & 32 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,16 +268,18 @@ void PrepareShutdown()
// CValidationInterface callbacks, flush them...
GetMainSignals().FlushBackgroundCallbacks();

if (!fLiteMode && !fRPCInWarmup) {
if (!fRPCInWarmup) {
// STORE DATA CACHES INTO SERIALIZED DAT FILES
CFlatDB<CMasternodeMetaMan> flatdb1("mncache.dat", "magicMasternodeCache");
flatdb1.Dump(mmetaman);
CFlatDB<CGovernanceManager> flatdb3("governance.dat", "magicGovernanceCache");
flatdb3.Dump(governance);
CFlatDB<CNetFulfilledRequestManager> flatdb4("netfulfilled.dat", "magicFulfilledCache");
flatdb4.Dump(netfulfilledman);
CFlatDB<CSporkManager> flatdb6("sporks.dat", "magicSporkCache");
flatdb6.Dump(sporkManager);
if (!fDisableGovernance) {
CFlatDB<CGovernanceManager> flatdb3("governance.dat", "magicGovernanceCache");
flatdb3.Dump(governance);
}
}

// After the threads that potentially access these pointers have been stopped,
Expand Down Expand Up @@ -490,7 +492,7 @@ std::string HelpMessage(HelpMessageMode mode)
#ifndef WIN32
strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), BITCOIN_PID_FILENAME));
#endif
strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. "
strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -rescan and -disablegovernance=false. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks"));
Expand Down Expand Up @@ -614,7 +616,7 @@ std::string HelpMessage(HelpMessageMode mode)
}
strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)"));
AppendParamsHelpMessages(strUsage, showDebug);
strUsage += HelpMessageOpt("-litemode", strprintf(_("Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u)"), 0));
strUsage += HelpMessageOpt("-disablegovernance", strprintf(_("Disable governance validation (0-1, default: %u)"), 0));
strUsage += HelpMessageOpt("-sporkaddr=<dashaddress>", strprintf(_("Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you.")));
strUsage += HelpMessageOpt("-minsporkkeys=<n>", strprintf(_("Overrides minimum spork signers to change spork value. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you.")));

Expand Down Expand Up @@ -980,6 +982,21 @@ void InitParameterInteraction()
LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__);
}

if (gArgs.GetBoolArg("-litemode", false)) {
if (gArgs.SoftSetBoolArg("-disablegovernance", true)) {
LogPrintf("%s: parameter interaction: -litemode=true -> setting -disablegovernance=true\n", __func__);
}
}

if (gArgs.GetArg("-prune", 0) > 0) {
if (gArgs.SoftSetBoolArg("-disablegovernance", true)) {
LogPrintf("%s: parameter interaction: -prune=%d -> setting -disablegovernance=true\n", __func__);
}
if (gArgs.SoftSetBoolArg("-txindex", false)) {
LogPrintf("%s: parameter interaction: -prune=%d -> setting -txindex=false\n", __func__);
}
}

// Make sure additional indexes are recalculated correctly in VerifyDB
// (we must reconnect blocks whenever we disconnect them for these indexes to work)
bool fAdditionalIndexes =
Expand Down Expand Up @@ -1095,10 +1112,13 @@ bool AppInitParameterInteraction()

// also see: InitParameterInteraction()

// if using block pruning, then disallow txindex
// if using block pruning, then disallow txindex and require disabling governance validation
if (gArgs.GetArg("-prune", 0)) {
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
if (!gArgs.GetBoolArg("-disablegovernance", false)) {
return InitError(_("Prune mode is incompatible with -disablegovernance=false."));
}
}

if (gArgs.IsArgSet("-devnet")) {
Expand Down Expand Up @@ -1490,11 +1510,23 @@ bool AppInitParameterInteraction()
if (gArgs.GetArg("-maxconnections", DEFAULT_MAX_PEER_CONNECTIONS) < DEFAULT_MAX_PEER_CONNECTIONS) {
return InitError(strprintf("Masternode must be able to handle at least %d connections, set -maxconnections=%d", DEFAULT_MAX_PEER_CONNECTIONS, DEFAULT_MAX_PEER_CONNECTIONS));
}
if (gArgs.GetBoolArg("-litemode", false)) {
return InitError(_("You can not start a masternode in lite mode."));
if (gArgs.GetBoolArg("-disablegovernance", false)) {
return InitError(_("You can not disable governance validation on a masternode."));
}
}

if (gArgs.IsArgSet("-litemode")) {
InitWarning(_("-litemode is deprecated.") + (gArgs.GetBoolArg("-litemode", false) ? (" " + _("Its replacement -disablegovernance has been forced instead.")) : ( " " + _("It has been replaced by -disablegovernance."))));
gArgs.ForceRemoveArg("-litemode");
}

fDisableGovernance = gArgs.GetBoolArg("-disablegovernance", false);
LogPrintf("fDisableGovernance %d\n", fDisableGovernance);

if (fDisableGovernance) {
InitWarning(_("You are starting with governance validation disabled.") + (fPruneMode ? " " + _("This is expected because you are running a pruned node.") : ""));
}

return true;
}

Expand Down Expand Up @@ -1764,22 +1796,12 @@ bool AppInitMain()
nMaxOutboundLimit = gArgs.GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024;
}

// ********************************************************* Step 7a: check lite mode and load sporks

// lite mode disables all Dash-specific functionality
fLiteMode = gArgs.GetBoolArg("-litemode", false);
LogPrintf("fLiteMode %d\n", fLiteMode);
// ********************************************************* Step 7a: Load sporks

if(fLiteMode) {
InitWarning(_("You are starting in lite mode, most Dash-specific functionality is disabled."));
}

if (!fLiteMode) {
uiInterface.InitMessage(_("Loading sporks cache..."));
CFlatDB<CSporkManager> flatdb6("sporks.dat", "magicSporkCache");
if (!flatdb6.Load(sporkManager)) {
return InitError(_("Failed to load sporks cache from") + "\n" + (GetDataDir() / "sporks.dat").string());
}
uiInterface.InitMessage(_("Loading sporks cache..."));
CFlatDB<CSporkManager> flatdb6("sporks.dat", "magicSporkCache");
if (!flatdb6.Load(sporkManager)) {
return InitError(_("Failed to load sporks cache from") + "\n" + (GetDataDir() / "sporks.dat").string());
}

// ********************************************************* Step 7b: load block chain
Expand Down Expand Up @@ -1853,9 +1875,9 @@ bool AppInitMain()
break;
}

if (!fLiteMode && !fTxIndex
if (!fDisableGovernance && !fTxIndex
&& chainparams.NetworkIDString() != CBaseChainParams::REGTEST) { // TODO remove this when pruning is fixed. See https://github.com/dashpay/dash/pull/1817 and https://github.com/dashpay/dash/pull/1743
return InitError(_("Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index."));
return InitError(_("Transaction index can't be disabled with governance validation enabled. Either start with -disablegovernance command line switch or enable transaction index."));
}

// If the loaded chain has a wrong genesis, bail out immediately
Expand Down Expand Up @@ -2093,7 +2115,7 @@ bool AppInitMain()

// LOAD SERIALIZED DAT FILES INTO DATA CACHES FOR INTERNAL USE

bool fLoadCacheFiles = !(fLiteMode || fReindex || fReindexChainState);
bool fLoadCacheFiles = !(fReindex || fReindexChainState);
{
LOCK(cs_main);
// was blocks/chainstate deleted?
Expand Down Expand Up @@ -2121,7 +2143,7 @@ bool AppInitMain()
strDBName = "governance.dat";
uiInterface.InitMessage(_("Loading governance cache..."));
CFlatDB<CGovernanceManager> flatdb3(strDBName, "magicGovernanceCache");
if (fLoadCacheFiles) {
if (fLoadCacheFiles && !fDisableGovernance) {
if(!flatdb3.Load(governance)) {
return InitError(_("Failed to load governance cache from") + "\n" + (pathDB / strDBName).string());
}
Expand Down Expand Up @@ -2149,15 +2171,14 @@ bool AppInitMain()

// ********************************************************* Step 10c: schedule Dash-specific tasks

if (!fLiteMode) {
scheduler.scheduleEvery(boost::bind(&CNetFulfilledRequestManager::DoMaintenance, boost::ref(netfulfilledman)), 60 * 1000);
scheduler.scheduleEvery(boost::bind(&CMasternodeSync::DoMaintenance, boost::ref(masternodeSync), boost::ref(*g_connman)), 1 * 1000);
scheduler.scheduleEvery(boost::bind(&CNetFulfilledRequestManager::DoMaintenance, boost::ref(netfulfilledman)), 60 * 1000);
scheduler.scheduleEvery(boost::bind(&CMasternodeSync::DoMaintenance, boost::ref(masternodeSync), boost::ref(*g_connman)), 1 * 1000);
scheduler.scheduleEvery(boost::bind(&CMasternodeUtils::DoMaintenance, boost::ref(*g_connman)), 1 * 1000);

if (!fDisableGovernance) {
scheduler.scheduleEvery(boost::bind(&CGovernanceManager::DoMaintenance, boost::ref(governance), boost::ref(*g_connman)), 60 * 5 * 1000);
}

scheduler.scheduleEvery(boost::bind(&CMasternodeUtils::DoMaintenance, boost::ref(*g_connman)), 1 * 1000);

if (fMasternodeMode) {
scheduler.scheduleEvery(boost::bind(&CPrivateSendServer::DoMaintenance, boost::ref(privateSendServer), boost::ref(*g_connman)), 1 * 1000);
}
Expand Down
Loading