dash/src/spork.cpp

494 lines
17 KiB
C++
Raw Normal View History

2016-12-20 14:26:45 +01:00
// Copyright (c) 2014-2017 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
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_18_QUORUM_DEBUG_ENABLED, 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
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()
{
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;
}
spork.Relay(connman);
LOCK(cs);
mapSporksByHash[spork.GetHash()] = spork;
mapSporksActive[nSporkID][keyIDSigner] = spork;
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;
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;
if (strName == "SPORK_18_QUORUM_DEBUG_ENABLED") return SPORK_18_QUORUM_DEBUG_ENABLED;
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";
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";
case SPORK_18_QUORUM_DEBUG_ENABLED: return "SPORK_18_QUORUM_DEBUG_ENABLED";
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
}