Skip to content

Commit

Permalink
Save/load spork cache (#2206)
Browse files Browse the repository at this point in the history
* CSporkManager class serialization

* Read/write sporks.dat file

* Move mapSporks into CSporkManager and serialize it

* fix GetSporkByHash

* spork tests

* add missed cs lock

* clear mapSporksByHash in CSporkManager::Clear

* use spork active rpc call to hide spork inner logic

* set small pause between rpc calls in cycles
  • Loading branch information
gladcow authored and UdjinM6 committed Aug 13, 2018
1 parent 7cf9572 commit 1767e34
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 9 deletions.
1 change: 1 addition & 0 deletions qa/pull-tester/rpc-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
'listsinceblock.py',
'p2p-leaktests.py',
'p2p-compactblocks.py',
'sporks.py',
]
if ENABLE_ZMQ:
testScripts.append('zmq_test.py')
Expand Down
87 changes: 87 additions & 0 deletions qa/rpc-tests/sporks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env python3
# Copyright (c) 2018 The Dash Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.

from test_framework.mininode import *
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from time import *

'''
'''

class SporkTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.num_nodes = 3
self.setup_clean_chain = True
self.is_network_split = False

def setup_network(self):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir,
["-debug", "-sporkkey=cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"]))
self.nodes.append(start_node(1, self.options.tmpdir,
["-debug"]))
self.nodes.append(start_node(2, self.options.tmpdir,
["-debug"]))
# connect only 2 first nodes at start
connect_nodes(self.nodes[0], 1)

def get_test_spork_state(self, node):
info = node.spork('active')
# use InstantSend spork for tests
return info['SPORK_2_INSTANTSEND_ENABLED']

def set_test_spork_state(self, node, state):
if state:
value = 0
else:
value = 4070908800
# use InstantSend spork for tests
node.spork('SPORK_2_INSTANTSEND_ENABLED', value)

def run_test(self):
# check test spork default state
assert(self.get_test_spork_state(self.nodes[0]))
assert(self.get_test_spork_state(self.nodes[1]))
assert(self.get_test_spork_state(self.nodes[2]))

# check spork propagation for connected nodes
self.set_test_spork_state(self.nodes[0], False)
start = time()
sent = False
while True:
if not self.get_test_spork_state(self.nodes[1]):
sent = True
break
if time() > start + 10:
break
sleep(0.1)
assert(sent)

# restart nodes to check spork persistence
stop_node(self.nodes[0], 0)
stop_node(self.nodes[1], 1)
self.nodes[0] = start_node(0, self.options.tmpdir, ["-debug"])
self.nodes[1] = start_node(1, self.options.tmpdir, ["-debug"])
assert(not self.get_test_spork_state(self.nodes[0]))
assert(not self.get_test_spork_state(self.nodes[1]))

# connect new node and check spork propagation after restoring from cache
connect_nodes(self.nodes[1], 2)
start = time()
sent = False
while True:
if not self.get_test_spork_state(self.nodes[2]):
sent = True
break
if time() > start + 10:
break
sleep(0.1)
assert(sent)


if __name__ == '__main__':
SporkTest().main()
9 changes: 9 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ void PrepareShutdown()
CFlatDB<CInstantSend> flatdb5("instantsend.dat", "magicInstantSendCache");
flatdb5.Dump(instantsend);
}
CFlatDB<CSporkManager> flatdb6("sporks.dat", "magicSporkCache");
flatdb6.Dump(sporkManager);
}

UnregisterNodeSignals(GetNodeSignals());
Expand Down Expand Up @@ -1960,6 +1962,13 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
return InitError(_("Failed to load InstantSend data cache from") + "\n" + (pathDB / strDBName).string());
}
}

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


Expand Down
10 changes: 7 additions & 3 deletions src/net_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,10 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return instantsend.AlreadyHave(inv.hash);

case MSG_SPORK:
return mapSporks.count(inv.hash);
{
CSporkMessage spork;
return sporkManager.GetSporkByHash(inv.hash, spork);
}

case MSG_MASTERNODE_PAYMENT_VOTE:
return mnpayments.mapMasternodePaymentVotes.count(inv.hash);
Expand Down Expand Up @@ -1165,8 +1168,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
}

if (!push && inv.type == MSG_SPORK) {
if(mapSporks.count(inv.hash)) {
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SPORK, mapSporks[inv.hash]));
CSporkMessage spork;
if(sporkManager.GetSporkByHash(inv.hash, spork)) {
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SPORK, spork));
push = true;
}
}
Expand Down
35 changes: 32 additions & 3 deletions src/spork.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

CSporkManager sporkManager;

std::map<uint256, CSporkMessage> mapSporks;
std::map<int, int64_t> mapSporkDefaults = {
{SPORK_2_INSTANTSEND_ENABLED, 0}, // ON
{SPORK_3_INSTANTSEND_BLOCK_FILTERING, 0}, // ON
Expand All @@ -28,6 +27,15 @@ std::map<int, int64_t> mapSporkDefaults = {
{SPORK_14_REQUIRE_SENTINEL_FLAG, 4070908800ULL}, // OFF
};

void CSporkManager::Clear()
{
LOCK(cs);
mapSporksActive.clear();
mapSporksByHash.clear();
sporkPubKeyID.SetNull();
sporkPrivKey = CKey();
}

void CSporkManager::ProcessSpork(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
{
if(fLiteMode) return; // disable all Dash specific functionality
Expand Down Expand Up @@ -69,7 +77,7 @@ void CSporkManager::ProcessSpork(CNode* pfrom, const std::string& strCommand, CD

{
LOCK(cs); // make sure to not lock this together with cs_main
mapSporks[hash] = spork;
mapSporksByHash[hash] = spork;
mapSporksActive[spork.nSporkID] = spork;
}
spork.Relay(connman);
Expand Down Expand Up @@ -122,7 +130,7 @@ bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue, CConnman& connman)
if(spork.Sign(sporkPrivKey)) {
spork.Relay(connman);
LOCK(cs);
mapSporks[spork.GetHash()] = spork;
mapSporksByHash[spork.GetHash()] = spork;
mapSporksActive[nSporkID] = spork;
return true;
}
Expand Down Expand Up @@ -197,6 +205,20 @@ std::string CSporkManager::GetSporkNameByID(int nSporkID)
}
}

bool CSporkManager::GetSporkByHash(const uint256& hash, CSporkMessage &sporkRet)
{
LOCK(cs);

const auto it = mapSporksByHash.find(hash);

if (it == mapSporksByHash.end())
return false;

sporkRet = it->second;

return true;
}

bool CSporkManager::SetSporkAddress(const std::string& strAddress) {
LOCK(cs);
CBitcoinAddress address(strAddress);
Expand Down Expand Up @@ -235,6 +257,13 @@ bool CSporkManager::SetPrivKey(const std::string& strPrivKey)
}
}

std::string CSporkManager::ToString() const
{
LOCK(cs);
return strprintf("Sporks: %llu", mapSporksActive.size());
}


uint256 CSporkMessage::GetHash() const
{
return SerializeHash(*this);
Expand Down
23 changes: 20 additions & 3 deletions src/spork.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ static const int SPORK_START = SPORK_
static const int SPORK_END = SPORK_14_REQUIRE_SENTINEL_FLAG;

extern std::map<int, int64_t> mapSporkDefaults;
extern std::map<uint256, CSporkMessage> mapSporks;
extern CSporkManager sporkManager;

//
Expand Down Expand Up @@ -86,8 +85,8 @@ class CSporkMessage
class CSporkManager
{
private:
CCriticalSection cs;
std::vector<unsigned char> vchSig;
mutable CCriticalSection cs;
std::map<uint256, CSporkMessage> mapSporksByHash;
std::map<int, CSporkMessage> mapSporksActive;

CKeyID sporkPubKeyID;
Expand All @@ -97,6 +96,20 @@ class CSporkManager

CSporkManager() {}

ADD_SERIALIZE_METHODS;

template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(sporkPubKeyID);
READWRITE(mapSporksByHash);
READWRITE(mapSporksActive);
// we don't serialize private key to prevent its leakage
}

void Clear();
/// Dummy implementation for CFlatDB
void CheckAndRemove() {}

void ProcessSpork(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
void ExecuteSpork(int nSporkID, int nValue);
bool UpdateSpork(int nSporkID, int64_t nValue, CConnman& connman);
Expand All @@ -106,8 +119,12 @@ class CSporkManager
int GetSporkIDByName(const std::string& strName);
std::string GetSporkNameByID(int nSporkID);

bool GetSporkByHash(const uint256& hash, CSporkMessage &sporkRet);

bool SetSporkAddress(const std::string& strAddress);
bool SetPrivKey(const std::string& strPrivKey);

std::string ToString() const;
};

#endif

0 comments on commit 1767e34

Please sign in to comment.