mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 12:02:48 +01:00
M-of-N-like sporks (#2288)
* support set of keys to sign spork * several addresses support in -sporkkey * tests for multykey sporks * command line option -minsporkkeys * make spork active only after given number of signers * use signature in spork hash calculation * test for new and old spork messages interaction * add multikeyspork.py to integration tests * change test to have ability to distinguish default spork value * require min spork keys number to be more than the half of the common spork keys number * calc current spork value with majority of signers * set test nodes time in integration test * extract keyid from signed spork message directly * change -sporkaddr option syntax to process several addresses * codestyle fixes * fix test comments * codestyle fixes * simplify CSporkManager::SporkValueIsActive * Calc signature hash without signature field * do not restore pubkey ids from cach * Calc different keyids to check signature because not all sporks can be synced at moment * codestyle fixes * Fix CSporkManager::CheckAndRemove to use several keys * codestyle fixes * Correct processing of not actual spork6 value with GetSignerKeyID
This commit is contained in:
parent
407baccec4
commit
f7ab6c469c
@ -124,6 +124,7 @@ testScripts = [
|
|||||||
'wallet-accounts.py',
|
'wallet-accounts.py',
|
||||||
'wallet-dump.py',
|
'wallet-dump.py',
|
||||||
'listtransactions.py',
|
'listtransactions.py',
|
||||||
|
'multikeysporks.py',
|
||||||
# vv Tests less than 60s vv
|
# vv Tests less than 60s vv
|
||||||
'sendheaders.py', # NOTE: needs dash_hash to pass
|
'sendheaders.py', # NOTE: needs dash_hash to pass
|
||||||
'zapwallettxes.py',
|
'zapwallettxes.py',
|
||||||
|
151
qa/rpc-tests/multikeysporks.py
Executable file
151
qa/rpc-tests/multikeysporks.py
Executable file
@ -0,0 +1,151 @@
|
|||||||
|
#!/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 *
|
||||||
|
|
||||||
|
'''
|
||||||
|
multikeysporks.py
|
||||||
|
|
||||||
|
Test logic for several signer keys usage for spork broadcast.
|
||||||
|
|
||||||
|
We set 5 possible keys for sporks signing and set minimum
|
||||||
|
required signers to 3. We check 1 and 2 signers can't set the spork
|
||||||
|
value, any 3 signers can change spork value and other 3 signers
|
||||||
|
can change it again.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
class MultiKeySporkTest(BitcoinTestFramework):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.num_nodes = 5
|
||||||
|
self.setup_clean_chain = True
|
||||||
|
self.is_network_split = False
|
||||||
|
|
||||||
|
def setup_network(self):
|
||||||
|
self.nodes = []
|
||||||
|
|
||||||
|
# secret(base58): 931wyuRNVYvhg18Uu9bky5Qg1z4QbxaJ7fefNBzjBPiLRqcd33F
|
||||||
|
# keyid(hex): 60f0f57f71f0081f1aacdd8432340a33a526f91b
|
||||||
|
# address(base58): yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa
|
||||||
|
|
||||||
|
# secret(base58): 91vbXGMSWKGHom62986XtL1q2mQDA12ngcuUNNe5NfMSj44j7g3
|
||||||
|
# keyid(hex): 43dff2b09de2f904f688ec14ee6899087b889ad0
|
||||||
|
# address(base58): yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h
|
||||||
|
|
||||||
|
# secret(base58): 92bxUjPT5AhgXuXJwfGGXqhomY2SdQ55MYjXyx9DZNxCABCSsRH
|
||||||
|
# keyid(hex): d9aa5fa00cce99101a4044e65dc544d1579890de
|
||||||
|
# address(base58): ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7
|
||||||
|
|
||||||
|
# secret(base58): 934yPXiVGf4RCY2qTs2Bt5k3TEtAiAg12sMxCt8yVWbSU7p3fuD
|
||||||
|
# keyid(hex): 0b23935ce0bea3b997a334f6fa276c9fa17687b2
|
||||||
|
# address(base58): ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn
|
||||||
|
|
||||||
|
# secret(base58): 92Cxwia363Wg2qGF1fE5z4GKi8u7r1nrWQXdtsj2ACZqaDPSihD
|
||||||
|
# keyid(hex): 1d1098b2b1f759b678a0a7a098637a9b898adcac
|
||||||
|
# address(base58): yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui
|
||||||
|
|
||||||
|
self.nodes.append(start_node(0, self.options.tmpdir,
|
||||||
|
["-debug", "-sporkkey=931wyuRNVYvhg18Uu9bky5Qg1z4QbxaJ7fefNBzjBPiLRqcd33F",
|
||||||
|
"-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7",
|
||||||
|
"-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h",
|
||||||
|
"-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa",
|
||||||
|
"-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn",
|
||||||
|
"-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui",
|
||||||
|
"-minsporkkeys=3"]))
|
||||||
|
self.nodes.append(start_node(1, self.options.tmpdir,
|
||||||
|
["-debug", "-sporkkey=91vbXGMSWKGHom62986XtL1q2mQDA12ngcuUNNe5NfMSj44j7g3",
|
||||||
|
"-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7",
|
||||||
|
"-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h",
|
||||||
|
"-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa",
|
||||||
|
"-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn",
|
||||||
|
"-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui",
|
||||||
|
"-minsporkkeys=3"]))
|
||||||
|
self.nodes.append(start_node(2, self.options.tmpdir,
|
||||||
|
["-debug", "-sporkkey=92bxUjPT5AhgXuXJwfGGXqhomY2SdQ55MYjXyx9DZNxCABCSsRH",
|
||||||
|
"-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7",
|
||||||
|
"-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h",
|
||||||
|
"-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa",
|
||||||
|
"-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn",
|
||||||
|
"-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui",
|
||||||
|
"-minsporkkeys=3"]))
|
||||||
|
self.nodes.append(start_node(3, self.options.tmpdir,
|
||||||
|
["-debug", "-sporkkey=934yPXiVGf4RCY2qTs2Bt5k3TEtAiAg12sMxCt8yVWbSU7p3fuD",
|
||||||
|
"-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7",
|
||||||
|
"-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h",
|
||||||
|
"-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa",
|
||||||
|
"-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn",
|
||||||
|
"-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui",
|
||||||
|
"-minsporkkeys=3"]))
|
||||||
|
self.nodes.append(start_node(4, self.options.tmpdir,
|
||||||
|
["-debug", "-sporkkey=92Cxwia363Wg2qGF1fE5z4GKi8u7r1nrWQXdtsj2ACZqaDPSihD",
|
||||||
|
"-sporkaddr=ygcG5S2pQz2U1UAaHvU6EznKZW7yapKMA7",
|
||||||
|
"-sporkaddr=yfLSXFfipnkgYioD6L8aUNyfRgEBuJv48h",
|
||||||
|
"-sporkaddr=yNsMZhEhYqv14TgdYb1NS2UmNZjE8FSJxa",
|
||||||
|
"-sporkaddr=ycbRQWbovrhQMTuxg9p4LAuW5SCMAKqPrn",
|
||||||
|
"-sporkaddr=yc5TGfcHYoLCrcbVy4umsiDjsYUn39vLui",
|
||||||
|
"-minsporkkeys=3"]))
|
||||||
|
# connect nodes at start
|
||||||
|
for i in range(0, 5):
|
||||||
|
for j in range(i, 5):
|
||||||
|
connect_nodes(self.nodes[i], j)
|
||||||
|
|
||||||
|
def get_test_spork_state(self, node):
|
||||||
|
info = node.spork('show')
|
||||||
|
# use InstantSend spork for tests
|
||||||
|
return info['SPORK_2_INSTANTSEND_ENABLED']
|
||||||
|
|
||||||
|
def set_test_spork_state(self, node, value):
|
||||||
|
# use InstantSend spork for tests
|
||||||
|
node.spork('SPORK_2_INSTANTSEND_ENABLED', value)
|
||||||
|
|
||||||
|
def wait_for_test_spork_state(self, node, value):
|
||||||
|
start = time()
|
||||||
|
got_state = False
|
||||||
|
while True:
|
||||||
|
if self.get_test_spork_state(node) == value:
|
||||||
|
got_state = True
|
||||||
|
break
|
||||||
|
if time() > start + 10:
|
||||||
|
break
|
||||||
|
sleep(0.1)
|
||||||
|
return got_state
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
# check test spork default state
|
||||||
|
for node in self.nodes:
|
||||||
|
assert(self.get_test_spork_state(node) == 0)
|
||||||
|
|
||||||
|
set_mocktime(get_mocktime() + 1)
|
||||||
|
set_node_times(self.nodes, get_mocktime())
|
||||||
|
# first and second signers set spork value
|
||||||
|
self.set_test_spork_state(self.nodes[0], 1)
|
||||||
|
self.set_test_spork_state(self.nodes[1], 1)
|
||||||
|
# spork change requires at least 3 signers
|
||||||
|
for node in self.nodes:
|
||||||
|
assert(not self.wait_for_test_spork_state(node, 1))
|
||||||
|
|
||||||
|
# third signer set spork value
|
||||||
|
self.set_test_spork_state(self.nodes[2], 1)
|
||||||
|
# now spork state is changed
|
||||||
|
for node in self.nodes:
|
||||||
|
assert(self.wait_for_test_spork_state(node, 1))
|
||||||
|
|
||||||
|
set_mocktime(get_mocktime() + 1)
|
||||||
|
set_node_times(self.nodes, get_mocktime())
|
||||||
|
# now set the spork again with other signers to test
|
||||||
|
# old and new spork messages interaction
|
||||||
|
self.set_test_spork_state(self.nodes[2], 2)
|
||||||
|
self.set_test_spork_state(self.nodes[3], 2)
|
||||||
|
self.set_test_spork_state(self.nodes[4], 2)
|
||||||
|
for node in self.nodes:
|
||||||
|
assert(self.wait_for_test_spork_state(node, 2))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
MultiKeySporkTest().main()
|
@ -237,7 +237,8 @@ public:
|
|||||||
nPoolMaxTransactions = 3;
|
nPoolMaxTransactions = 3;
|
||||||
nFulfilledRequestExpireTime = 60*60; // fulfilled requests expire in 1 hour
|
nFulfilledRequestExpireTime = 60*60; // fulfilled requests expire in 1 hour
|
||||||
|
|
||||||
strSporkAddress = "Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh";
|
vSporkAddresses = {"Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh"};
|
||||||
|
nMinSporkKeys = 1;
|
||||||
|
|
||||||
checkpointData = (CCheckpointData) {
|
checkpointData = (CCheckpointData) {
|
||||||
boost::assign::map_list_of
|
boost::assign::map_list_of
|
||||||
@ -393,7 +394,8 @@ public:
|
|||||||
nPoolMaxTransactions = 3;
|
nPoolMaxTransactions = 3;
|
||||||
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes
|
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes
|
||||||
|
|
||||||
strSporkAddress = "yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55";
|
vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"};
|
||||||
|
nMinSporkKeys = 1;
|
||||||
|
|
||||||
checkpointData = (CCheckpointData) {
|
checkpointData = (CCheckpointData) {
|
||||||
boost::assign::map_list_of
|
boost::assign::map_list_of
|
||||||
@ -537,7 +539,8 @@ public:
|
|||||||
nPoolMaxTransactions = 3;
|
nPoolMaxTransactions = 3;
|
||||||
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes
|
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes
|
||||||
|
|
||||||
strSporkAddress = "yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55";
|
vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"};
|
||||||
|
nMinSporkKeys = 1;
|
||||||
|
|
||||||
checkpointData = (CCheckpointData) {
|
checkpointData = (CCheckpointData) {
|
||||||
boost::assign::map_list_of
|
boost::assign::map_list_of
|
||||||
@ -643,7 +646,8 @@ public:
|
|||||||
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes
|
nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes
|
||||||
|
|
||||||
// privKey: cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK
|
// privKey: cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK
|
||||||
strSporkAddress = "yj949n1UH6fDhw6HtVE5VMj2iSTaSWBMcW";
|
vSporkAddresses = {"yj949n1UH6fDhw6HtVE5VMj2iSTaSWBMcW"};
|
||||||
|
nMinSporkKeys = 1;
|
||||||
|
|
||||||
checkpointData = (CCheckpointData){
|
checkpointData = (CCheckpointData){
|
||||||
boost::assign::map_list_of
|
boost::assign::map_list_of
|
||||||
|
@ -88,7 +88,8 @@ public:
|
|||||||
const ChainTxData& TxData() const { return chainTxData; }
|
const ChainTxData& TxData() const { return chainTxData; }
|
||||||
int PoolMaxTransactions() const { return nPoolMaxTransactions; }
|
int PoolMaxTransactions() const { return nPoolMaxTransactions; }
|
||||||
int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; }
|
int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; }
|
||||||
const std::string& SporkAddress() const { return strSporkAddress; }
|
const std::vector<std::string>& SporkAddresses() const { return vSporkAddresses; }
|
||||||
|
int MinSporkKeys() const { return nMinSporkKeys; }
|
||||||
protected:
|
protected:
|
||||||
CChainParams() {}
|
CChainParams() {}
|
||||||
|
|
||||||
@ -116,7 +117,8 @@ protected:
|
|||||||
ChainTxData chainTxData;
|
ChainTxData chainTxData;
|
||||||
int nPoolMaxTransactions;
|
int nPoolMaxTransactions;
|
||||||
int nFulfilledRequestExpireTime;
|
int nFulfilledRequestExpireTime;
|
||||||
std::string strSporkAddress;
|
std::vector<std::string> vSporkAddresses;
|
||||||
|
int nMinSporkKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
26
src/init.cpp
26
src/init.cpp
@ -570,6 +570,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
|||||||
AppendParamsHelpMessages(strUsage, showDebug);
|
AppendParamsHelpMessages(strUsage, showDebug);
|
||||||
strUsage += HelpMessageOpt("-litemode=<n>", strprintf(_("Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u)"), 0));
|
strUsage += HelpMessageOpt("-litemode=<n>", strprintf(_("Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u)"), 0));
|
||||||
strUsage += HelpMessageOpt("-sporkaddr=<hex>", strprintf(_("Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you.")));
|
strUsage += HelpMessageOpt("-sporkaddr=<hex>", 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.")));
|
||||||
|
|
||||||
strUsage += HelpMessageGroup(_("Masternode options:"));
|
strUsage += HelpMessageGroup(_("Masternode options:"));
|
||||||
strUsage += HelpMessageOpt("-masternode=<n>", strprintf(_("Enable the client to act as a masternode (0-1, default: %u)"), 0));
|
strUsage += HelpMessageOpt("-masternode=<n>", strprintf(_("Enable the client to act as a masternode (0-1, default: %u)"), 0));
|
||||||
@ -1398,13 +1399,28 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
|||||||
threadGroup.create_thread(&ThreadScriptCheck);
|
threadGroup.create_thread(&ThreadScriptCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sporkManager.SetSporkAddress(GetArg("-sporkaddr", Params().SporkAddress())))
|
std::vector<std::string> vSporkAddresses;
|
||||||
return InitError(_("Invalid spork address specified with -sporkaddr"));
|
if (mapMultiArgs.count("-sporkaddr")) {
|
||||||
|
vSporkAddresses = mapMultiArgs.at("-sporkaddr");
|
||||||
|
} else {
|
||||||
|
vSporkAddresses = Params().SporkAddresses();
|
||||||
|
}
|
||||||
|
for (const auto& address: vSporkAddresses) {
|
||||||
|
if (!sporkManager.SetSporkAddress(address)) {
|
||||||
|
return InitError(_("Invalid spork address specified with -sporkaddr"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (IsArgSet("-sporkkey")) // spork priv key
|
int minsporkkeys = GetArg("-minsporkkeys", Params().MinSporkKeys());
|
||||||
{
|
if (!sporkManager.SetMinSporkKeys(minsporkkeys)) {
|
||||||
if (!sporkManager.SetPrivKey(GetArg("-sporkkey", "")))
|
return InitError(_("Invalid minimum number of spork signers specified with -minsporkkeys"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (IsArgSet("-sporkkey")) { // spork priv key
|
||||||
|
if (!sporkManager.SetPrivKey(GetArg("-sporkkey", ""))) {
|
||||||
return InitError(_("Unable to sign spork message, wrong key?"));
|
return InitError(_("Unable to sign spork message, wrong key?"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the lightweight task scheduler thread
|
// Start the lightweight task scheduler thread
|
||||||
|
188
src/spork.cpp
188
src/spork.cpp
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
CSporkManager sporkManager;
|
CSporkManager sporkManager;
|
||||||
|
|
||||||
const std::string CSporkManager::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-1";
|
const std::string CSporkManager::SERIALIZATION_VERSION_STRING = "CMasternodeMan-Version-2";
|
||||||
|
|
||||||
std::map<int, int64_t> mapSporkDefaults = {
|
std::map<int, int64_t> mapSporkDefaults = {
|
||||||
{SPORK_2_INSTANTSEND_ENABLED, 0}, // ON
|
{SPORK_2_INSTANTSEND_ENABLED, 0}, // ON
|
||||||
@ -31,6 +31,27 @@ std::map<int, int64_t> mapSporkDefaults = {
|
|||||||
{SPORK_16_INSTANTSEND_AUTOLOCKS, 4070908800ULL}, // OFF
|
{SPORK_16_INSTANTSEND_AUTOLOCKS, 4070908800ULL}, // OFF
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool CSporkManager::SporkValueIsActive(int nSporkID, int64_t &nActiveValueRet) const
|
||||||
|
{
|
||||||
|
LOCK(cs);
|
||||||
|
|
||||||
|
if (!mapSporksActive.count(nSporkID)) return false;
|
||||||
|
|
||||||
|
// calc how many values we have and how many signers vote for every value
|
||||||
|
std::map<int64_t, int> mapValueCounts;
|
||||||
|
for (const auto& pair: mapSporksActive.at(nSporkID)) {
|
||||||
|
mapValueCounts[pair.second.nValue]++;
|
||||||
|
if (mapValueCounts.at(pair.second.nValue) >= nMinSporkKeys) {
|
||||||
|
// nMinSporkKeys is always more than the half of the max spork keys number,
|
||||||
|
// so there is only one such value and we can stop here
|
||||||
|
nActiveValueRet = pair.second.nValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CSporkManager::Clear()
|
void CSporkManager::Clear()
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
@ -43,29 +64,47 @@ void CSporkManager::Clear()
|
|||||||
void CSporkManager::CheckAndRemove()
|
void CSporkManager::CheckAndRemove()
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
bool fSporkAddressIsSet = !sporkPubKeyID.IsNull();
|
bool fSporkAddressIsSet = !setSporkPubKeyIDs.empty();
|
||||||
assert(fSporkAddressIsSet);
|
assert(fSporkAddressIsSet);
|
||||||
|
|
||||||
auto itActive = mapSporksActive.begin();
|
auto itActive = mapSporksActive.begin();
|
||||||
while (itActive != mapSporksActive.end()) {
|
while (itActive != mapSporksActive.end()) {
|
||||||
if (!itActive->second.CheckSignature(sporkPubKeyID, false)) {
|
auto itSignerPair = itActive->second.begin();
|
||||||
if (!itActive->second.CheckSignature(sporkPubKeyID, true)) {
|
while (itSignerPair != itActive->second.end()) {
|
||||||
mapSporksByHash.erase(itActive->second.GetHash());
|
if (setSporkPubKeyIDs.find(itSignerPair->first) == setSporkPubKeyIDs.end()) {
|
||||||
mapSporksActive.erase(itActive++);
|
mapSporksByHash.erase(itSignerPair->second.GetHash());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!itSignerPair->second.CheckSignature(itSignerPair->first, false)) {
|
||||||
|
if (!itSignerPair->second.CheckSignature(itSignerPair->first, true)) {
|
||||||
|
mapSporksByHash.erase(itSignerPair->second.GetHash());
|
||||||
|
itActive->second.erase(itSignerPair++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++itSignerPair;
|
||||||
|
}
|
||||||
|
if (itActive->second.empty()) {
|
||||||
|
mapSporksActive.erase(itActive++);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
++itActive;
|
++itActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto itByHash = mapSporksByHash.begin();
|
auto itByHash = mapSporksByHash.begin();
|
||||||
while (itByHash != mapSporksByHash.end()) {
|
while (itByHash != mapSporksByHash.end()) {
|
||||||
if (!itByHash->second.CheckSignature(sporkPubKeyID, false)) {
|
bool found = false;
|
||||||
if (!itByHash->second.CheckSignature(sporkPubKeyID, true)) {
|
for (const auto& signer: setSporkPubKeyIDs) {
|
||||||
mapSporksActive.erase(itByHash->second.nSporkID);
|
if (itByHash->second.CheckSignature(signer, false) ||
|
||||||
mapSporksByHash.erase(itByHash++);
|
itByHash->second.CheckSignature(signer, true)) {
|
||||||
continue;
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!found) {
|
||||||
|
mapSporksByHash.erase(itByHash++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
++itByHash;
|
++itByHash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,41 +127,61 @@ void CSporkManager::ProcessSpork(CNode* pfrom, const std::string& strCommand, CD
|
|||||||
if(!chainActive.Tip()) return;
|
if(!chainActive.Tip()) return;
|
||||||
strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->id);
|
strLogMsg = strprintf("SPORK -- hash: %s id: %d value: %10d bestHeight: %d peer=%d", hash.ToString(), spork.nSporkID, spork.nValue, chainActive.Height(), pfrom->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CKeyID keyIDSigner;
|
||||||
|
bool fSpork6IsActive = IsSporkActive(SPORK_6_NEW_SIGS);
|
||||||
|
if (!spork.GetSignerKeyID(keyIDSigner, fSpork6IsActive)
|
||||||
|
|| !setSporkPubKeyIDs.count(keyIDSigner)) {
|
||||||
|
// Note: unlike for other messages we have to check for new format even with SPORK_6_NEW_SIGS
|
||||||
|
// inactive because SPORK_6_NEW_SIGS default is OFF and it is not the first spork to sync
|
||||||
|
// (and even if it would, spork order can't be guaranteed anyway).
|
||||||
|
if (!spork.GetSignerKeyID(keyIDSigner, !fSpork6IsActive)
|
||||||
|
|| !setSporkPubKeyIDs.count(keyIDSigner)) {
|
||||||
|
LOCK(cs_main);
|
||||||
|
LogPrintf("CSporkManager::ProcessSpork -- ERROR: invalid signature\n");
|
||||||
|
Misbehaving(pfrom->GetId(), 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(cs); // make sure to not lock this together with cs_main
|
LOCK(cs); // make sure to not lock this together with cs_main
|
||||||
if (mapSporksActive.count(spork.nSporkID)) {
|
if (mapSporksActive.count(spork.nSporkID)) {
|
||||||
if (mapSporksActive[spork.nSporkID].nTimeSigned >= spork.nTimeSigned) {
|
if (mapSporksActive[spork.nSporkID].count(keyIDSigner)) {
|
||||||
LogPrint("spork", "%s seen\n", strLogMsg);
|
if (mapSporksActive[spork.nSporkID][keyIDSigner].nTimeSigned >= spork.nTimeSigned) {
|
||||||
return;
|
LogPrint("spork", "%s seen\n", strLogMsg);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
LogPrintf("%s updated\n", strLogMsg);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LogPrintf("%s updated\n", strLogMsg);
|
LogPrintf("%s new signer\n", strLogMsg);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LogPrintf("%s new\n", strLogMsg);
|
LogPrintf("%s new\n", strLogMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!spork.CheckSignature(sporkPubKeyID, IsSporkActive(SPORK_6_NEW_SIGS))) {
|
|
||||||
LOCK(cs_main);
|
|
||||||
LogPrintf("CSporkManager::ProcessSpork -- ERROR: invalid signature\n");
|
|
||||||
Misbehaving(pfrom->GetId(), 100);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(cs); // make sure to not lock this together with cs_main
|
LOCK(cs); // make sure to not lock this together with cs_main
|
||||||
mapSporksByHash[hash] = spork;
|
mapSporksByHash[hash] = spork;
|
||||||
mapSporksActive[spork.nSporkID] = spork;
|
mapSporksActive[spork.nSporkID][keyIDSigner] = spork;
|
||||||
}
|
}
|
||||||
spork.Relay(connman);
|
spork.Relay(connman);
|
||||||
|
|
||||||
//does a task if needed
|
//does a task if needed
|
||||||
ExecuteSpork(spork.nSporkID, spork.nValue);
|
int64_t nActiveValue = 0;
|
||||||
|
if (SporkValueIsActive(spork.nSporkID, nActiveValue)) {
|
||||||
|
ExecuteSpork(spork.nSporkID, nActiveValue);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strCommand == NetMsgType::GETSPORKS) {
|
} else if (strCommand == NetMsgType::GETSPORKS) {
|
||||||
LOCK(cs); // make sure to not lock this together with cs_main
|
LOCK(cs); // make sure to not lock this together with cs_main
|
||||||
for (const auto& pair : mapSporksActive) {
|
for (const auto& pair : mapSporksActive) {
|
||||||
connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SPORK, pair.second));
|
for (const auto& signerSporkPair: pair.second) {
|
||||||
|
connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SPORK, signerSporkPair.second));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,11 +220,17 @@ bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue, CConnman& connman)
|
|||||||
{
|
{
|
||||||
CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetAdjustedTime());
|
CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetAdjustedTime());
|
||||||
|
|
||||||
if(spork.Sign(sporkPrivKey, IsSporkActive(SPORK_6_NEW_SIGS))) {
|
bool fSpork6IsActive = IsSporkActive(SPORK_6_NEW_SIGS);
|
||||||
|
if(spork.Sign(sporkPrivKey, fSpork6IsActive)) {
|
||||||
|
CKeyID keyIDSigner;
|
||||||
|
if (!spork.GetSignerKeyID(keyIDSigner, fSpork6IsActive) || !setSporkPubKeyIDs.count(keyIDSigner)) {
|
||||||
|
LogPrintf("CSporkManager::UpdateSpork: failed to find keyid for private key\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
spork.Relay(connman);
|
spork.Relay(connman);
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
mapSporksByHash[spork.GetHash()] = spork;
|
mapSporksByHash[spork.GetHash()] = spork;
|
||||||
mapSporksActive[nSporkID] = spork;
|
mapSporksActive[nSporkID][keyIDSigner] = spork;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,26 +241,29 @@ bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue, CConnman& connman)
|
|||||||
bool CSporkManager::IsSporkActive(int nSporkID)
|
bool CSporkManager::IsSporkActive(int nSporkID)
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
int64_t r = -1;
|
int64_t nSporkValue = -1;
|
||||||
|
|
||||||
if(mapSporksActive.count(nSporkID)){
|
if (SporkValueIsActive(nSporkID, nSporkValue)) {
|
||||||
r = mapSporksActive[nSporkID].nValue;
|
return nSporkValue < GetAdjustedTime();
|
||||||
} else if (mapSporkDefaults.count(nSporkID)) {
|
|
||||||
r = mapSporkDefaults[nSporkID];
|
|
||||||
} else {
|
|
||||||
LogPrint("spork", "CSporkManager::IsSporkActive -- Unknown Spork ID %d\n", nSporkID);
|
|
||||||
r = 4070908800ULL; // 2099-1-1 i.e. off by default
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r < GetAdjustedTime();
|
if (mapSporkDefaults.count(nSporkID)) {
|
||||||
|
return mapSporkDefaults[nSporkID] < GetAdjustedTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
LogPrint("spork", "CSporkManager::IsSporkActive -- Unknown Spork ID %d\n", nSporkID);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab the value of the spork on the network, or the default
|
// grab the value of the spork on the network, or the default
|
||||||
int64_t CSporkManager::GetSporkValue(int nSporkID)
|
int64_t CSporkManager::GetSporkValue(int nSporkID)
|
||||||
{
|
{
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
if (mapSporksActive.count(nSporkID))
|
|
||||||
return mapSporksActive[nSporkID].nValue;
|
int64_t nSporkValue = -1;
|
||||||
|
if (SporkValueIsActive(nSporkID, nSporkValue)) {
|
||||||
|
return nSporkValue;
|
||||||
|
}
|
||||||
|
|
||||||
if (mapSporkDefaults.count(nSporkID)) {
|
if (mapSporkDefaults.count(nSporkID)) {
|
||||||
return mapSporkDefaults[nSporkID];
|
return mapSporkDefaults[nSporkID];
|
||||||
@ -260,10 +328,23 @@ bool CSporkManager::GetSporkByHash(const uint256& hash, CSporkMessage &sporkRet)
|
|||||||
bool CSporkManager::SetSporkAddress(const std::string& strAddress) {
|
bool CSporkManager::SetSporkAddress(const std::string& strAddress) {
|
||||||
LOCK(cs);
|
LOCK(cs);
|
||||||
CBitcoinAddress address(strAddress);
|
CBitcoinAddress address(strAddress);
|
||||||
if (!address.IsValid() || !address.GetKeyID(sporkPubKeyID)) {
|
CKeyID keyid;
|
||||||
|
if (!address.IsValid() || !address.GetKeyID(keyid)) {
|
||||||
LogPrintf("CSporkManager::SetSporkAddress -- Failed to parse spork address\n");
|
LogPrintf("CSporkManager::SetSporkAddress -- Failed to parse spork address\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
setSporkPubKeyIDs.insert(keyid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSporkManager::SetMinSporkKeys(int minSporkKeys)
|
||||||
|
{
|
||||||
|
int maxKeysNumber = setSporkPubKeyIDs.size();
|
||||||
|
if ((minSporkKeys <= maxKeysNumber / 2) || (minSporkKeys > maxKeysNumber)) {
|
||||||
|
LogPrintf("CSporkManager::SetMinSporkKeys -- Invalid min spork signers number: %d\n", minSporkKeys);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
nMinSporkKeys = minSporkKeys;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,8 +357,8 @@ bool CSporkManager::SetPrivKey(const std::string& strPrivKey)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pubKey.GetID() != sporkPubKeyID) {
|
if (setSporkPubKeyIDs.find(pubKey.GetID()) == setSporkPubKeyIDs.end()) {
|
||||||
LogPrintf("CSporkManager::SetPrivKey -- New private key does not belong to spork address\n");
|
LogPrintf("CSporkManager::SetPrivKey -- New private key does not belong to spork addresses\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +390,11 @@ uint256 CSporkMessage::GetHash() const
|
|||||||
|
|
||||||
uint256 CSporkMessage::GetSignatureHash() const
|
uint256 CSporkMessage::GetSignatureHash() const
|
||||||
{
|
{
|
||||||
return GetHash();
|
CHashWriter s(SER_GETHASH, 0);
|
||||||
|
s << nSporkID;
|
||||||
|
s << nValue;
|
||||||
|
s << nTimeSigned;
|
||||||
|
return s.GetHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSporkMessage::Sign(const CKey& key, bool fSporkSixActive)
|
bool CSporkMessage::Sign(const CKey& key, bool fSporkSixActive)
|
||||||
@ -382,6 +467,27 @@ bool CSporkMessage::CheckSignature(const CKeyID& pubKeyId, bool fSporkSixActive)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CSporkMessage::GetSignerKeyID(CKeyID &retKeyidSporkSigner, bool fSporkSixActive)
|
||||||
|
{
|
||||||
|
CPubKey pubkeyFromSig;
|
||||||
|
if (fSporkSixActive) {
|
||||||
|
if (!pubkeyFromSig.RecoverCompact(GetSignatureHash(), vchSig)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::string strMessage = std::to_string(nSporkID) + std::to_string(nValue) + std::to_string(nTimeSigned);
|
||||||
|
CHashWriter ss(SER_GETHASH, 0);
|
||||||
|
ss << strMessageMagic;
|
||||||
|
ss << strMessage;
|
||||||
|
if (!pubkeyFromSig.RecoverCompact(ss.GetHash(), vchSig)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retKeyidSporkSigner = pubkeyFromSig.GetID();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CSporkMessage::Relay(CConnman& connman)
|
void CSporkMessage::Relay(CConnman& connman)
|
||||||
{
|
{
|
||||||
CInv inv(MSG_SPORK, GetHash());
|
CInv inv(MSG_SPORK, GetHash());
|
||||||
|
16
src/spork.h
16
src/spork.h
@ -70,9 +70,7 @@ public:
|
|||||||
READWRITE(nSporkID);
|
READWRITE(nSporkID);
|
||||||
READWRITE(nValue);
|
READWRITE(nValue);
|
||||||
READWRITE(nTimeSigned);
|
READWRITE(nTimeSigned);
|
||||||
if (!(s.GetType() & SER_GETHASH)) {
|
READWRITE(vchSig);
|
||||||
READWRITE(vchSig);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint256 GetHash() const;
|
uint256 GetHash() const;
|
||||||
@ -80,6 +78,7 @@ public:
|
|||||||
|
|
||||||
bool Sign(const CKey& key, bool fSporkSixActive);
|
bool Sign(const CKey& key, bool fSporkSixActive);
|
||||||
bool CheckSignature(const CKeyID& pubKeyId, bool fSporkSixActive) const;
|
bool CheckSignature(const CKeyID& pubKeyId, bool fSporkSixActive) const;
|
||||||
|
bool GetSignerKeyID(CKeyID& retKeyidSporkSigner, bool fSporkSixActive);
|
||||||
void Relay(CConnman& connman);
|
void Relay(CConnman& connman);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -91,11 +90,13 @@ private:
|
|||||||
|
|
||||||
mutable CCriticalSection cs;
|
mutable CCriticalSection cs;
|
||||||
std::map<uint256, CSporkMessage> mapSporksByHash;
|
std::map<uint256, CSporkMessage> mapSporksByHash;
|
||||||
std::map<int, CSporkMessage> mapSporksActive;
|
std::map<int, std::map<CKeyID, CSporkMessage> > mapSporksActive;
|
||||||
|
|
||||||
CKeyID sporkPubKeyID;
|
std::set<CKeyID> setSporkPubKeyIDs;
|
||||||
|
int nMinSporkKeys;
|
||||||
CKey sporkPrivKey;
|
CKey sporkPrivKey;
|
||||||
|
|
||||||
|
bool SporkValueIsActive(int nSporkID, int64_t& nActiveValueRet) const;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CSporkManager() {}
|
CSporkManager() {}
|
||||||
@ -114,7 +115,9 @@ public:
|
|||||||
strVersion = SERIALIZATION_VERSION_STRING;
|
strVersion = SERIALIZATION_VERSION_STRING;
|
||||||
READWRITE(strVersion);
|
READWRITE(strVersion);
|
||||||
}
|
}
|
||||||
READWRITE(sporkPubKeyID);
|
// we don't serialize pubkey ids because pubkeys should be
|
||||||
|
// hardcoded or be setted with cmdline or options, should
|
||||||
|
// not reuse pubkeys from previous dashd run
|
||||||
READWRITE(mapSporksByHash);
|
READWRITE(mapSporksByHash);
|
||||||
READWRITE(mapSporksActive);
|
READWRITE(mapSporksActive);
|
||||||
// we don't serialize private key to prevent its leakage
|
// we don't serialize private key to prevent its leakage
|
||||||
@ -135,6 +138,7 @@ public:
|
|||||||
bool GetSporkByHash(const uint256& hash, CSporkMessage &sporkRet);
|
bool GetSporkByHash(const uint256& hash, CSporkMessage &sporkRet);
|
||||||
|
|
||||||
bool SetSporkAddress(const std::string& strAddress);
|
bool SetSporkAddress(const std::string& strAddress);
|
||||||
|
bool SetMinSporkKeys(int minSporkKeys);
|
||||||
bool SetPrivKey(const std::string& strPrivKey);
|
bool SetPrivKey(const std::string& strPrivKey);
|
||||||
|
|
||||||
std::string ToString() const;
|
std::string ToString() const;
|
||||||
|
Loading…
Reference in New Issue
Block a user