dash/src/spork.cpp

500 lines
18 KiB
C++
Raw Normal View History

// Copyright (c) 2014-2019 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2015-02-09 21:54:51 +01:00
#include "spork.h"
#include "base58.h"
#include "chainparams.h"
#include "validation.h"
#include "messagesigner.h"
#include "net_processing.h"
#include "netmessagemaker.h"
2015-02-09 21:54:51 +01:00
#include <string>
2015-02-09 21:54:51 +01:00
CSporkManager sporkManager;
const std::string CSporkManager::SERIALIZATION_VERSION_STRING = "CSporkManager-Version-2";
2018-09-26 16:15:02 +02:00
std::map<int, int64_t> mapSporkDefaults = {
{SPORK_2_INSTANTSEND_ENABLED, 0}, // ON
{SPORK_3_INSTANTSEND_BLOCK_FILTERING, 0}, // ON
{SPORK_5_INSTANTSEND_MAX_VALUE, 1000}, // 1000 Dash
{SPORK_6_NEW_SIGS, 4070908800ULL}, // OFF
{SPORK_9_SUPERBLOCKS_ENABLED, 4070908800ULL}, // OFF
{SPORK_12_RECONSIDER_BLOCKS, 0}, // 0 BLOCKS
{SPORK_15_DETERMINISTIC_MNS_ENABLED, 4070908800ULL}, // OFF
Automatic InstantSend locks for "simple" transactions (#2140) * add locktransaction rpc call * Remove special instantsend fee for simple transactions * Function to check if trx is simple enough to be autolocked * Automatic lock for all received from peers simple trxes If we get a new transaction with CInv message and it is "simple" and is accepted in mempool, we initiate its lock. We don't lock orphan trxes that accepted in mempool after this trx because they are locked by other peers. * Automatically lock simple trxes in wallet * protocol bump for InstantSend without special fee * Add function to detect used mempool share * Mempool threshold for auto IX locks * Add SPORK_16_INSTANTSEND_AUTOLOCKS spork * Make autolocks active only when spork SPORK_16_INSTANTSEND_AUTOLOCKS is active * BIP9 autolocks activation * revert increasing min peer protocol version for mn rank * move IsTrxSimple check to CTxLockRequest class * make MAX_INPUTS_FOR_AUTO_IX private member of CTxLockRequest class * make AUTO_IX_MEMPOOL_THRESHOLD private member of CInstantSend class * remove locktransaction RPC call * tests for automatic IS locks * fix mempool threshod calculation * bump mocktime in activate_autoix_bip9 * set node times * no need to spam the node with gettransaction rpc requests that often * use `spork active` instead of leaking spork logic into tests * codestyle fixes * add test description in comments * fix typo * sync test nodes more often during BIP9 activation * Use 4th bit in BIP9 activation * Fix comments according codestyle guide * Call AcceptLockRequest and Vote at the first node creating autoix lock * fix mempool used memory calculation * rallback not necessary change in CWallet::CreateTransaction * test for stopping autolocks for full mempool * Inject "simple autolockable" txes into txlockrequest logic
2018-09-26 16:17:47 +02:00
{SPORK_16_INSTANTSEND_AUTOLOCKS, 4070908800ULL}, // OFF
{SPORK_17_QUORUM_DKG_ENABLED, 4070908800ULL}, // OFF
{SPORK_19_CHAINLOCKS_ENABLED, 4070908800ULL}, // OFF
{SPORK_20_INSTANTSEND_LLMQ_BASED, 4070908800ULL}, // OFF
};
2015-02-09 21:54:51 +01:00
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
Collection of minor performance optimizations (#2855) * Merge #13176: Improve CRollingBloomFilter performance: replace modulus with FastMod 9aac9f90d5e56752cc6cbfac48063ad29a01143c replace modulus with FastMod (Martin Ankerl) Pull request description: Not sure if this is optimization is necessary, but anyway I have some spare time so here it is. This replaces the slow modulo operation with a much faster 64bit multiplication & shift. This works when the hash is uniformly distributed between 0 and 2^32-1. This speeds up the benchmark by a factor of about 1.3: ``` RollingBloom, 5, 1500000, 3.73733, 4.97569e-07, 4.99002e-07, 4.98372e-07 # before RollingBloom, 5, 1500000, 2.86842, 3.81630e-07, 3.83730e-07, 3.82473e-07 # FastMod ``` Be aware that this changes the internal data of the filter, so this should probably not be used for CBloomFilter because of interoperability problems. Tree-SHA512: 04104f3fb09f56c9d14458a6aad919aeb0a5af944e8ee6a31f00e93c753e22004648c1cd65bf36752b6addec528d19fb665c27b955ce1666a85a928e17afa47a * Use unordered_map in CSporkManager In one of my profiling sessions with many InstantSend transactions happening, calls into CSporkManager added up to about 1% of total CPU time. This is easily avoidable by using unordered maps. * Use std::unordered_map instead of std::map in limitedmap * Use unordered_set for CNode::setAskFor * Add serialization support for unordered maps and sets * Use unordered_map for mapArgs and mapMultiArgs * Let limitedmap prune in batches and use unordered_multimap Due to the batched pruning, there is no need to maintain an ordered map of values anymore. Only when nPruneAfterSize, there is a need to create a temporary ordered vector of values to figure out what can be removed. * Instead of using a multimap for mapAskFor, use a vector which we sort on demand CNode::AskFor will now push entries into an initially unordered vector instead of an ordered multimap. Only when we later want to use vecAskFor in SendMessages, we sort the vector. The vector will actually be mostly sorted in most cases as insertion order usually mimics the desired ordering. Only the last few entries might need some shuffling around. Doing the sort on-demand should be less wasteful then trying to maintain correct order all the time. * Fix compilation of tests * Fix limitedmap tests * Rename limitedmap to unordered_limitedmap to ensure backports conflict This ensures that future backports that depends on limitedmap's ordering conflict so that we are made aware of needed action. * Fix compilation error on Travis
2019-04-11 14:42:14 +02:00
std::unordered_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()
{
LOCK(cs);
mapSporksActive.clear();
mapSporksByHash.clear();
// sporkPubKeyID and sporkPrivKey should be set in init.cpp,
// we should not alter them here.
}
2018-09-26 16:15:02 +02:00
void CSporkManager::CheckAndRemove()
{
LOCK(cs);
bool fSporkAddressIsSet = !setSporkPubKeyIDs.empty();
2018-09-26 16:15:02 +02:00
assert(fSporkAddressIsSet);
auto itActive = mapSporksActive.begin();
while (itActive != mapSporksActive.end()) {
auto itSignerPair = itActive->second.begin();
while (itSignerPair != itActive->second.end()) {
if (setSporkPubKeyIDs.find(itSignerPair->first) == setSporkPubKeyIDs.end()) {
mapSporksByHash.erase(itSignerPair->second.GetHash());
2018-09-26 16:15:02 +02:00
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;
2018-09-26 16:15:02 +02:00
}
++itActive;
}
2018-09-26 16:15:02 +02:00
auto itByHash = mapSporksByHash.begin();
while (itByHash != mapSporksByHash.end()) {
bool found = false;
for (const auto& signer: setSporkPubKeyIDs) {
if (itByHash->second.CheckSignature(signer, false) ||
itByHash->second.CheckSignature(signer, true)) {
found = true;
break;
2018-09-26 16:15:02 +02:00
}
}
if (!found) {
mapSporksByHash.erase(itByHash++);
continue;
}
2018-09-26 16:15:02 +02:00
++itByHash;
}
}
void CSporkManager::ProcessSpork(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
2015-02-09 21:54:51 +01:00
{
if(fLiteMode) return; // disable all Dash specific functionality
2015-02-09 21:54:51 +01:00
if (strCommand == NetMsgType::SPORK) {
2015-02-09 21:54:51 +01:00
CSporkMessage spork;
vRecv >> spork;
uint256 hash = spork.GetHash();
std::string strLogMsg;
{
LOCK(cs_main);
connman.RemoveAskFor(hash);
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);
}
if (spork.nTimeSigned > GetAdjustedTime() + 2 * 60 * 60) {
LOCK(cs_main);
LogPrintf("CSporkManager::ProcessSpork -- ERROR: too far into the future\n");
Misbehaving(pfrom->GetId(), 100);
return;
}
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
if (mapSporksActive.count(spork.nSporkID)) {
if (mapSporksActive[spork.nSporkID].count(keyIDSigner)) {
if (mapSporksActive[spork.nSporkID][keyIDSigner].nTimeSigned >= spork.nTimeSigned) {
LogPrint("spork", "%s seen\n", strLogMsg);
return;
} else {
LogPrintf("%s updated\n", strLogMsg);
}
} else {
LogPrintf("%s new signer\n", strLogMsg);
}
2015-02-09 21:54:51 +01:00
} else {
LogPrintf("%s new\n", strLogMsg);
2015-02-09 21:54:51 +01:00
}
}
{
LOCK(cs); // make sure to not lock this together with cs_main
mapSporksByHash[hash] = spork;
mapSporksActive[spork.nSporkID][keyIDSigner] = spork;
}
spork.Relay(connman);
2015-02-09 21:54:51 +01:00
2015-02-12 05:05:09 +01:00
//does a task if needed
int64_t nActiveValue = 0;
if (SporkValueIsActive(spork.nSporkID, nActiveValue)) {
ExecuteSpork(spork.nSporkID, nActiveValue);
}
} else if (strCommand == NetMsgType::GETSPORKS) {
LOCK(cs); // make sure to not lock this together with cs_main
for (const auto& pair : mapSporksActive) {
for (const auto& signerSporkPair: pair.second) {
connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SPORK, signerSporkPair.second));
}
2015-02-09 21:54:51 +01:00
}
}
}
void CSporkManager::ExecuteSpork(int nSporkID, int nValue)
{
//correct fork via spork technology
if(nSporkID == SPORK_12_RECONSIDER_BLOCKS && nValue > 0) {
// allow to reprocess 24h of blocks max, which should be enough to resolve any issues
int64_t nMaxBlocks = 576;
// this potentially can be a heavy operation, so only allow this to be executed once per 10 minutes
int64_t nTimeout = 10 * 60;
static int64_t nTimeExecuted = 0; // i.e. it was never executed before
if(GetTime() - nTimeExecuted < nTimeout) {
LogPrint("spork", "CSporkManager::ExecuteSpork -- ERROR: Trying to reconsider blocks, too soon - %d/%d\n", GetTime() - nTimeExecuted, nTimeout);
return;
}
if(nValue > nMaxBlocks) {
LogPrintf("CSporkManager::ExecuteSpork -- ERROR: Trying to reconsider too many blocks %d/%d\n", nValue, nMaxBlocks);
return;
}
LogPrintf("CSporkManager::ExecuteSpork -- Reconsider Last %d Blocks\n", nValue);
ReprocessBlocks(nValue);
nTimeExecuted = GetTime();
}
}
bool CSporkManager::UpdateSpork(int nSporkID, int64_t nValue, CConnman& connman)
{
CSporkMessage spork = CSporkMessage(nSporkID, nValue, GetAdjustedTime());
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;
}
{
LOCK(cs);
mapSporksByHash[spork.GetHash()] = spork;
mapSporksActive[nSporkID][keyIDSigner] = spork;
}
spork.Relay(connman);
return true;
}
return false;
}
bool CSporkManager::IsSporkActive(int nSporkID)
2015-02-09 21:54:51 +01:00
{
LOCK(cs);
int64_t nSporkValue = -1;
2015-02-09 21:54:51 +01:00
if (SporkValueIsActive(nSporkID, nSporkValue)) {
return nSporkValue < GetAdjustedTime();
2015-02-09 21:54:51 +01:00
}
if (mapSporkDefaults.count(nSporkID)) {
return mapSporkDefaults[nSporkID] < GetAdjustedTime();
}
LogPrint("spork", "CSporkManager::IsSporkActive -- Unknown Spork ID %d\n", nSporkID);
return false;
2015-02-09 21:54:51 +01:00
}
int64_t CSporkManager::GetSporkValue(int nSporkID)
2015-02-11 15:47:21 +01:00
{
LOCK(cs);
int64_t nSporkValue = -1;
if (SporkValueIsActive(nSporkID, nSporkValue)) {
return nSporkValue;
}
if (mapSporkDefaults.count(nSporkID)) {
return mapSporkDefaults[nSporkID];
2015-02-11 15:47:21 +01:00
}
LogPrint("spork", "CSporkManager::GetSporkValue -- Unknown Spork ID %d\n", nSporkID);
return -1;
2015-02-11 15:47:21 +01:00
}
int CSporkManager::GetSporkIDByName(const std::string& strName)
2015-02-12 05:05:09 +01:00
{
if (strName == "SPORK_2_INSTANTSEND_ENABLED") return SPORK_2_INSTANTSEND_ENABLED;
if (strName == "SPORK_3_INSTANTSEND_BLOCK_FILTERING") return SPORK_3_INSTANTSEND_BLOCK_FILTERING;
if (strName == "SPORK_5_INSTANTSEND_MAX_VALUE") return SPORK_5_INSTANTSEND_MAX_VALUE;
if (strName == "SPORK_6_NEW_SIGS") return SPORK_6_NEW_SIGS;
if (strName == "SPORK_9_SUPERBLOCKS_ENABLED") return SPORK_9_SUPERBLOCKS_ENABLED;
if (strName == "SPORK_12_RECONSIDER_BLOCKS") return SPORK_12_RECONSIDER_BLOCKS;
if (strName == "SPORK_15_DETERMINISTIC_MNS_ENABLED") return SPORK_15_DETERMINISTIC_MNS_ENABLED;
Automatic InstantSend locks for "simple" transactions (#2140) * add locktransaction rpc call * Remove special instantsend fee for simple transactions * Function to check if trx is simple enough to be autolocked * Automatic lock for all received from peers simple trxes If we get a new transaction with CInv message and it is "simple" and is accepted in mempool, we initiate its lock. We don't lock orphan trxes that accepted in mempool after this trx because they are locked by other peers. * Automatically lock simple trxes in wallet * protocol bump for InstantSend without special fee * Add function to detect used mempool share * Mempool threshold for auto IX locks * Add SPORK_16_INSTANTSEND_AUTOLOCKS spork * Make autolocks active only when spork SPORK_16_INSTANTSEND_AUTOLOCKS is active * BIP9 autolocks activation * revert increasing min peer protocol version for mn rank * move IsTrxSimple check to CTxLockRequest class * make MAX_INPUTS_FOR_AUTO_IX private member of CTxLockRequest class * make AUTO_IX_MEMPOOL_THRESHOLD private member of CInstantSend class * remove locktransaction RPC call * tests for automatic IS locks * fix mempool threshod calculation * bump mocktime in activate_autoix_bip9 * set node times * no need to spam the node with gettransaction rpc requests that often * use `spork active` instead of leaking spork logic into tests * codestyle fixes * add test description in comments * fix typo * sync test nodes more often during BIP9 activation * Use 4th bit in BIP9 activation * Fix comments according codestyle guide * Call AcceptLockRequest and Vote at the first node creating autoix lock * fix mempool used memory calculation * rallback not necessary change in CWallet::CreateTransaction * test for stopping autolocks for full mempool * Inject "simple autolockable" txes into txlockrequest logic
2018-09-26 16:17:47 +02:00
if (strName == "SPORK_16_INSTANTSEND_AUTOLOCKS") return SPORK_16_INSTANTSEND_AUTOLOCKS;
if (strName == "SPORK_17_QUORUM_DKG_ENABLED") return SPORK_17_QUORUM_DKG_ENABLED;
2019-01-23 17:40:37 +01:00
if (strName == "SPORK_19_CHAINLOCKS_ENABLED") return SPORK_19_CHAINLOCKS_ENABLED;
if (strName == "SPORK_20_INSTANTSEND_LLMQ_BASED") return SPORK_20_INSTANTSEND_LLMQ_BASED;
LogPrint("spork", "CSporkManager::GetSporkIDByName -- Unknown Spork name '%s'\n", strName);
return -1;
2015-08-03 01:08:37 +02:00
}
2015-08-02 23:59:28 +02:00
std::string CSporkManager::GetSporkNameByID(int nSporkID)
{
switch (nSporkID) {
case SPORK_2_INSTANTSEND_ENABLED: return "SPORK_2_INSTANTSEND_ENABLED";
case SPORK_3_INSTANTSEND_BLOCK_FILTERING: return "SPORK_3_INSTANTSEND_BLOCK_FILTERING";
case SPORK_5_INSTANTSEND_MAX_VALUE: return "SPORK_5_INSTANTSEND_MAX_VALUE";
case SPORK_6_NEW_SIGS: return "SPORK_6_NEW_SIGS";
case SPORK_9_SUPERBLOCKS_ENABLED: return "SPORK_9_SUPERBLOCKS_ENABLED";
case SPORK_12_RECONSIDER_BLOCKS: return "SPORK_12_RECONSIDER_BLOCKS";
case SPORK_15_DETERMINISTIC_MNS_ENABLED: return "SPORK_15_DETERMINISTIC_MNS_ENABLED";
Automatic InstantSend locks for "simple" transactions (#2140) * add locktransaction rpc call * Remove special instantsend fee for simple transactions * Function to check if trx is simple enough to be autolocked * Automatic lock for all received from peers simple trxes If we get a new transaction with CInv message and it is "simple" and is accepted in mempool, we initiate its lock. We don't lock orphan trxes that accepted in mempool after this trx because they are locked by other peers. * Automatically lock simple trxes in wallet * protocol bump for InstantSend without special fee * Add function to detect used mempool share * Mempool threshold for auto IX locks * Add SPORK_16_INSTANTSEND_AUTOLOCKS spork * Make autolocks active only when spork SPORK_16_INSTANTSEND_AUTOLOCKS is active * BIP9 autolocks activation * revert increasing min peer protocol version for mn rank * move IsTrxSimple check to CTxLockRequest class * make MAX_INPUTS_FOR_AUTO_IX private member of CTxLockRequest class * make AUTO_IX_MEMPOOL_THRESHOLD private member of CInstantSend class * remove locktransaction RPC call * tests for automatic IS locks * fix mempool threshod calculation * bump mocktime in activate_autoix_bip9 * set node times * no need to spam the node with gettransaction rpc requests that often * use `spork active` instead of leaking spork logic into tests * codestyle fixes * add test description in comments * fix typo * sync test nodes more often during BIP9 activation * Use 4th bit in BIP9 activation * Fix comments according codestyle guide * Call AcceptLockRequest and Vote at the first node creating autoix lock * fix mempool used memory calculation * rallback not necessary change in CWallet::CreateTransaction * test for stopping autolocks for full mempool * Inject "simple autolockable" txes into txlockrequest logic
2018-09-26 16:17:47 +02:00
case SPORK_16_INSTANTSEND_AUTOLOCKS: return "SPORK_16_INSTANTSEND_AUTOLOCKS";
case SPORK_17_QUORUM_DKG_ENABLED: return "SPORK_17_QUORUM_DKG_ENABLED";
2019-01-23 17:40:37 +01:00
case SPORK_19_CHAINLOCKS_ENABLED: return "SPORK_19_CHAINLOCKS_ENABLED";
case SPORK_20_INSTANTSEND_LLMQ_BASED: return "SPORK_20_INSTANTSEND_LLMQ_BASED";
default:
LogPrint("spork", "CSporkManager::GetSporkNameByID -- Unknown Spork ID %d\n", nSporkID);
return "Unknown";
}
2015-02-12 05:05:09 +01:00
}
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);
CKeyID keyid;
if (!address.IsValid() || !address.GetKeyID(keyid)) {
LogPrintf("CSporkManager::SetSporkAddress -- Failed to parse spork address\n");
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;
}
bool CSporkManager::SetPrivKey(const std::string& strPrivKey)
2015-02-09 21:54:51 +01:00
{
CKey key;
CPubKey pubKey;
if(!CMessageSigner::GetKeysFromSecret(strPrivKey, key, pubKey)) {
LogPrintf("CSporkManager::SetPrivKey -- Failed to parse private key\n");
return false;
}
2015-02-09 21:54:51 +01:00
if (setSporkPubKeyIDs.find(pubKey.GetID()) == setSporkPubKeyIDs.end()) {
LogPrintf("CSporkManager::SetPrivKey -- New private key does not belong to spork addresses\n");
return false;
}
CSporkMessage spork;
if (spork.Sign(key, IsSporkActive(SPORK_6_NEW_SIGS))) {
LOCK(cs);
// Test signing successful, proceed
LogPrintf("CSporkManager::SetPrivKey -- Successfully initialized as spork signer\n");
sporkPrivKey = key;
return true;
} else {
LogPrintf("CSporkManager::SetPrivKey -- Test signing failed\n");
2015-02-09 21:54:51 +01:00
return false;
}
}
std::string CSporkManager::ToString() const
{
LOCK(cs);
return strprintf("Sporks: %llu", mapSporksActive.size());
}
uint256 CSporkMessage::GetHash() const
{
return SerializeHash(*this);
}
uint256 CSporkMessage::GetSignatureHash() const
{
CHashWriter s(SER_GETHASH, 0);
s << nSporkID;
s << nValue;
s << nTimeSigned;
return s.GetHash();
}
bool CSporkMessage::Sign(const CKey& key, bool fSporkSixActive)
2015-02-09 21:54:51 +01:00
{
if (!key.IsValid()) {
LogPrintf("CSporkMessage::Sign -- signing key is not valid\n");
return false;
}
CKeyID pubKeyId = key.GetPubKey().GetID();
std::string strError = "";
2015-02-09 21:54:51 +01:00
if (fSporkSixActive) {
uint256 hash = GetSignatureHash();
2015-02-09 21:54:51 +01:00
if(!CHashSigner::SignHash(hash, key, vchSig)) {
LogPrintf("CSporkMessage::Sign -- SignHash() failed\n");
return false;
}
if (!CHashSigner::VerifyHash(hash, pubKeyId, vchSig, strError)) {
LogPrintf("CSporkMessage::Sign -- VerifyHash() failed, error: %s\n", strError);
return false;
}
} else {
std::string strMessage = std::to_string(nSporkID) + std::to_string(nValue) + std::to_string(nTimeSigned);
if(!CMessageSigner::SignMessage(strMessage, vchSig, key)) {
LogPrintf("CSporkMessage::Sign -- SignMessage() failed\n");
return false;
}
if(!CMessageSigner::VerifyMessage(pubKeyId, vchSig, strMessage, strError)) {
LogPrintf("CSporkMessage::Sign -- VerifyMessage() failed, error: %s\n", strError);
return false;
}
2015-02-09 21:54:51 +01:00
}
return true;
}
bool CSporkMessage::CheckSignature(const CKeyID& pubKeyId, bool fSporkSixActive) const
2015-02-09 21:54:51 +01:00
{
std::string strError = "";
2015-02-09 21:54:51 +01:00
if (fSporkSixActive) {
uint256 hash = GetSignatureHash();
if (!CHashSigner::VerifyHash(hash, pubKeyId, vchSig, strError)) {
// Note: unlike for many other messages when SPORK_6_NEW_SIGS is ON sporks with sigs in old format
// and newer timestamps should not be accepted, so if we failed here - that's it
LogPrintf("CSporkMessage::CheckSignature -- VerifyHash() failed, error: %s\n", strError);
return false;
}
} else {
std::string strMessage = std::to_string(nSporkID) + std::to_string(nValue) + std::to_string(nTimeSigned);
if (!CMessageSigner::VerifyMessage(pubKeyId, vchSig, strMessage, strError)){
// 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).
uint256 hash = GetSignatureHash();
if (!CHashSigner::VerifyHash(hash, pubKeyId, vchSig, strError)) {
LogPrintf("CSporkMessage::CheckSignature -- VerifyHash() failed, error: %s\n", strError);
return false;
}
}
2015-02-09 21:54:51 +01:00
}
return true;
2015-02-09 21:54:51 +01:00
}
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)
2015-02-09 21:54:51 +01:00
{
CInv inv(MSG_SPORK, GetHash());
connman.RelayInv(inv);
2015-04-03 00:51:08 +02:00
}