d2ca9eddee
* Check resulting signature for validity when signing with BLS operator keys * Support specifying proTxHash when using "gobject vote-alias" The "alias-name" is now interpreted as proTxHash when deterministic MN lists are active. The classical "alias" as found in masternode.conf has no meaning after spork15 activation. * Don't use masternode.conf list in gobject_vote_many when spork15 is active Keys don't match anymore so we should only rely on the deterministic list and the local wallet's keys. * Check for pwalletMain not being null in gobject_vote_many * Allow to use vote-conf for non-proposal votes when spork15 is active This fixes sentinel not being able to create triggers on testnet. * Fix error message * Fix no-wallet compilation and check for pwalletMain at runtime
316 lines
10 KiB
C++
316 lines
10 KiB
C++
// Copyright (c) 2014-2018 The Dash Core developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "governance-vote.h"
|
|
#include "governance-object.h"
|
|
#include "masternode-sync.h"
|
|
#include "masternodeman.h"
|
|
#include "messagesigner.h"
|
|
#include "util.h"
|
|
|
|
std::string CGovernanceVoting::ConvertOutcomeToString(vote_outcome_enum_t nOutcome)
|
|
{
|
|
static const std::map<vote_outcome_enum_t, std::string> mapOutcomeString = {
|
|
{ VOTE_OUTCOME_NONE, "none" },
|
|
{ VOTE_OUTCOME_YES, "yes" },
|
|
{ VOTE_OUTCOME_NO, "no" },
|
|
{ VOTE_OUTCOME_ABSTAIN, "abstain" } };
|
|
|
|
const auto& it = mapOutcomeString.find(nOutcome);
|
|
if (it == mapOutcomeString.end()) {
|
|
LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown outcome %d\n", __func__, nOutcome);
|
|
return "error";
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
std::string CGovernanceVoting::ConvertSignalToString(vote_signal_enum_t nSignal)
|
|
{
|
|
static const std::map<vote_signal_enum_t, std::string> mapSignalsString = {
|
|
{ VOTE_SIGNAL_FUNDING, "funding" },
|
|
{ VOTE_SIGNAL_VALID, "valid" },
|
|
{ VOTE_SIGNAL_DELETE, "delete" },
|
|
{ VOTE_SIGNAL_ENDORSED, "endorsed" } };
|
|
|
|
const auto& it = mapSignalsString.find(nSignal);
|
|
if (it == mapSignalsString.end()) {
|
|
LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown signal %d\n", __func__, nSignal);
|
|
return "none";
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
|
|
vote_outcome_enum_t CGovernanceVoting::ConvertVoteOutcome(const std::string& strVoteOutcome)
|
|
{
|
|
static const std::map<std::string, vote_outcome_enum_t> mapStringOutcome = {
|
|
{ "none", VOTE_OUTCOME_NONE },
|
|
{ "yes", VOTE_OUTCOME_YES },
|
|
{ "no", VOTE_OUTCOME_NO },
|
|
{ "abstain", VOTE_OUTCOME_ABSTAIN } };
|
|
|
|
const auto& it = mapStringOutcome.find(strVoteOutcome);
|
|
if (it == mapStringOutcome.end()) {
|
|
LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown outcome %s\n", __func__, strVoteOutcome);
|
|
return VOTE_OUTCOME_NONE;
|
|
}
|
|
return it->second;
|
|
|
|
}
|
|
|
|
vote_signal_enum_t CGovernanceVoting::ConvertVoteSignal(const std::string& strVoteSignal)
|
|
{
|
|
static const std::map<std::string, vote_signal_enum_t> mapStrVoteSignals = {
|
|
{"funding", VOTE_SIGNAL_FUNDING},
|
|
{"valid", VOTE_SIGNAL_VALID},
|
|
{"delete", VOTE_SIGNAL_DELETE},
|
|
{"endorsed", VOTE_SIGNAL_ENDORSED}};
|
|
|
|
const auto& it = mapStrVoteSignals.find(strVoteSignal);
|
|
if (it == mapStrVoteSignals.end()) {
|
|
LogPrintf("CGovernanceVoting::%s -- ERROR: Unknown signal %s\n", __func__, strVoteSignal);
|
|
return VOTE_SIGNAL_NONE;
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
CGovernanceVote::CGovernanceVote() :
|
|
fValid(true),
|
|
fSynced(false),
|
|
nVoteSignal(int(VOTE_SIGNAL_NONE)),
|
|
masternodeOutpoint(),
|
|
nParentHash(),
|
|
nVoteOutcome(int(VOTE_OUTCOME_NONE)),
|
|
nTime(0),
|
|
vchSig()
|
|
{
|
|
}
|
|
|
|
CGovernanceVote::CGovernanceVote(const COutPoint& outpointMasternodeIn, const uint256& nParentHashIn, vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) :
|
|
fValid(true),
|
|
fSynced(false),
|
|
nVoteSignal(eVoteSignalIn),
|
|
masternodeOutpoint(outpointMasternodeIn),
|
|
nParentHash(nParentHashIn),
|
|
nVoteOutcome(eVoteOutcomeIn),
|
|
nTime(GetAdjustedTime()),
|
|
vchSig()
|
|
{
|
|
UpdateHash();
|
|
}
|
|
|
|
std::string CGovernanceVote::ToString() const
|
|
{
|
|
std::ostringstream ostr;
|
|
ostr << masternodeOutpoint.ToStringShort() << ":"
|
|
<< nTime << ":"
|
|
<< CGovernanceVoting::ConvertOutcomeToString(GetOutcome()) << ":"
|
|
<< CGovernanceVoting::ConvertSignalToString(GetSignal());
|
|
return ostr.str();
|
|
}
|
|
|
|
void CGovernanceVote::Relay(CConnman& connman) const
|
|
{
|
|
// Do not relay until fully synced
|
|
if (!masternodeSync.IsSynced()) {
|
|
LogPrint("gobject", "CGovernanceVote::Relay -- won't relay until fully synced\n");
|
|
return;
|
|
}
|
|
|
|
CInv inv(MSG_GOVERNANCE_OBJECT_VOTE, GetHash());
|
|
connman.RelayInv(inv, MIN_GOVERNANCE_PEER_PROTO_VERSION);
|
|
}
|
|
|
|
void CGovernanceVote::UpdateHash() const
|
|
{
|
|
// Note: doesn't match serialization
|
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << masternodeOutpoint << uint8_t{} << 0xffffffff; // adding dummy values here to match old hashing format
|
|
ss << nParentHash;
|
|
ss << nVoteSignal;
|
|
ss << nVoteOutcome;
|
|
ss << nTime;
|
|
*const_cast<uint256*>(&hash) = ss.GetHash();
|
|
}
|
|
|
|
uint256 CGovernanceVote::GetHash() const
|
|
{
|
|
return hash;
|
|
}
|
|
|
|
uint256 CGovernanceVote::GetSignatureHash() const
|
|
{
|
|
return SerializeHash(*this);
|
|
}
|
|
|
|
bool CGovernanceVote::Sign(const CKey& key, const CKeyID& keyID)
|
|
{
|
|
std::string strError;
|
|
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
uint256 hash = GetSignatureHash();
|
|
|
|
if (!CHashSigner::SignHash(hash, key, vchSig)) {
|
|
LogPrintf("CGovernanceVote::Sign -- SignHash() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!CHashSigner::VerifyHash(hash, keyID, vchSig, strError)) {
|
|
LogPrintf("CGovernanceVote::Sign -- VerifyHash() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
} else {
|
|
std::string strMessage = masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" +
|
|
std::to_string(nVoteSignal) + "|" + std::to_string(nVoteOutcome) + "|" + std::to_string(nTime);
|
|
|
|
if (!CMessageSigner::SignMessage(strMessage, vchSig, key)) {
|
|
LogPrintf("CGovernanceVote::Sign -- SignMessage() failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) {
|
|
LogPrintf("CGovernanceVote::Sign -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGovernanceVote::CheckSignature(const CKeyID& keyID) const
|
|
{
|
|
std::string strError;
|
|
|
|
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
|
|
uint256 hash = GetSignatureHash();
|
|
|
|
if (!CHashSigner::VerifyHash(hash, keyID, vchSig, strError)) {
|
|
// could be a signature in old format
|
|
std::string strMessage = masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" +
|
|
std::to_string(nVoteSignal) + "|" +
|
|
std::to_string(nVoteOutcome) + "|" +
|
|
std::to_string(nTime);
|
|
|
|
if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) {
|
|
// nope, not in old format either
|
|
LogPrint("gobject", "CGovernanceVote::IsValid -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
std::string strMessage = masternodeOutpoint.ToStringShort() + "|" + nParentHash.ToString() + "|" +
|
|
std::to_string(nVoteSignal) + "|" +
|
|
std::to_string(nVoteOutcome) + "|" +
|
|
std::to_string(nTime);
|
|
|
|
if (!CMessageSigner::VerifyMessage(keyID, vchSig, strMessage, strError)) {
|
|
LogPrint("gobject", "CGovernanceVote::IsValid -- VerifyMessage() failed, error: %s\n", strError);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGovernanceVote::Sign(const CBLSSecretKey& key)
|
|
{
|
|
uint256 hash = GetSignatureHash();
|
|
CBLSSignature sig = key.Sign(hash);
|
|
if (!sig.IsValid()) {
|
|
return false;
|
|
}
|
|
sig.GetBuf(vchSig);
|
|
return true;
|
|
}
|
|
|
|
bool CGovernanceVote::CheckSignature(const CBLSPublicKey& pubKey) const
|
|
{
|
|
uint256 hash = GetSignatureHash();
|
|
CBLSSignature sig;
|
|
sig.SetBuf(vchSig);
|
|
if (!sig.VerifyInsecure(pubKey, hash)) {
|
|
LogPrintf("CGovernanceVote::CheckSignature -- VerifyInsecure() failed\n");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CGovernanceVote::IsValid(bool useVotingKey) const
|
|
{
|
|
if (nTime > GetAdjustedTime() + (60 * 60)) {
|
|
LogPrint("gobject", "CGovernanceVote::IsValid -- vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", GetHash().ToString(), nTime, GetAdjustedTime() + (60 * 60));
|
|
return false;
|
|
}
|
|
|
|
// support up to MAX_SUPPORTED_VOTE_SIGNAL, can be extended
|
|
if (nVoteSignal > MAX_SUPPORTED_VOTE_SIGNAL) {
|
|
LogPrint("gobject", "CGovernanceVote::IsValid -- Client attempted to vote on invalid signal(%d) - %s\n", nVoteSignal, GetHash().ToString());
|
|
return false;
|
|
}
|
|
|
|
// 0=none, 1=yes, 2=no, 3=abstain. Beyond that reject votes
|
|
if (nVoteOutcome > 3) {
|
|
LogPrint("gobject", "CGovernanceVote::IsValid -- Client attempted to vote on invalid outcome(%d) - %s\n", nVoteSignal, GetHash().ToString());
|
|
return false;
|
|
}
|
|
|
|
masternode_info_t infoMn;
|
|
if (!mnodeman.GetMasternodeInfo(masternodeOutpoint, infoMn)) {
|
|
LogPrint("gobject", "CGovernanceVote::IsValid -- Unknown Masternode - %s\n", masternodeOutpoint.ToStringShort());
|
|
return false;
|
|
}
|
|
|
|
if (useVotingKey) {
|
|
return CheckSignature(infoMn.keyIDVoting);
|
|
} else {
|
|
if (deterministicMNManager->IsDeterministicMNsSporkActive()) {
|
|
return CheckSignature(infoMn.blsPubKeyOperator);
|
|
} else {
|
|
return CheckSignature(infoMn.legacyKeyIDOperator);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool operator==(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
|
|
{
|
|
bool fResult = ((vote1.masternodeOutpoint == vote2.masternodeOutpoint) &&
|
|
(vote1.nParentHash == vote2.nParentHash) &&
|
|
(vote1.nVoteOutcome == vote2.nVoteOutcome) &&
|
|
(vote1.nVoteSignal == vote2.nVoteSignal) &&
|
|
(vote1.nTime == vote2.nTime));
|
|
return fResult;
|
|
}
|
|
|
|
bool operator<(const CGovernanceVote& vote1, const CGovernanceVote& vote2)
|
|
{
|
|
bool fResult = (vote1.masternodeOutpoint < vote2.masternodeOutpoint);
|
|
if (!fResult) {
|
|
return false;
|
|
}
|
|
fResult = (vote1.masternodeOutpoint == vote2.masternodeOutpoint);
|
|
|
|
fResult = fResult && (vote1.nParentHash < vote2.nParentHash);
|
|
if (!fResult) {
|
|
return false;
|
|
}
|
|
fResult = fResult && (vote1.nParentHash == vote2.nParentHash);
|
|
|
|
fResult = fResult && (vote1.nVoteOutcome < vote2.nVoteOutcome);
|
|
if (!fResult) {
|
|
return false;
|
|
}
|
|
fResult = fResult && (vote1.nVoteOutcome == vote2.nVoteOutcome);
|
|
|
|
fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal);
|
|
if (!fResult) {
|
|
return false;
|
|
}
|
|
fResult = fResult && (vote1.nVoteSignal == vote2.nVoteSignal);
|
|
|
|
fResult = fResult && (vote1.nTime < vote2.nTime);
|
|
|
|
return fResult;
|
|
}
|