dash/src/instantx.cpp

1244 lines
48 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/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "activemasternode.h"
#include "instantx.h"
#include "key.h"
#include "validation.h"
#include "masternode-payments.h"
#include "masternode-sync.h"
#include "masternodeman.h"
#include "messagesigner.h"
#include "net.h"
#include "netmessagemaker.h"
#include "protocol.h"
#include "spork.h"
#include "sync.h"
#include "txmempool.h"
#include "util.h"
#include "consensus/validation.h"
#include "validationinterface.h"
#include "warnings.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif // ENABLE_WALLET
#include <boost/algorithm/string/replace.hpp>
#include <boost/thread.hpp>
#ifdef ENABLE_WALLET
extern CWallet* pwalletMain;
#endif // ENABLE_WALLET
extern CTxMemPool mempool;
bool fEnableInstantSend = true;
int nInstantSendDepth = DEFAULT_INSTANTSEND_DEPTH;
int nCompleteTXLocks;
CInstantSend instantsend;
const std::string CInstantSend::SERIALIZATION_VERSION_STRING = "CInstantSend-Version-1";
// Transaction Locks
//
// step 1) Some node announces intention to lock transaction inputs via "txlockrequest" message (ix)
// step 2) Top COutPointLock::SIGNATURES_TOTAL masternodes per each spent outpoint push "txlockvote" message (txlvote)
// step 3) Once there are COutPointLock::SIGNATURES_REQUIRED valid "txlockvote" messages (txlvote) per each spent outpoint
// for a corresponding "txlockrequest" message (ix), all outpoints from that tx are treated as locked
//
// CInstantSend
//
void CInstantSend::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
{
if(fLiteMode) return; // disable all Dash specific functionality
if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return;
// NOTE: NetMsgType::TXLOCKREQUEST is handled via ProcessMessage() in net_processing.cpp
if (strCommand == NetMsgType::TXLOCKVOTE) // InstantSend Transaction Lock Consensus Votes
{
if(pfrom->nVersion < MIN_INSTANTSEND_PROTO_VERSION) {
LogPrint("instantsend", "TXLOCKVOTE -- peer=%d using obsolete version %i\n", pfrom->id, pfrom->nVersion);
connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE,
strprintf("Version must be %d or greater", MIN_INSTANTSEND_PROTO_VERSION)));
return;
}
CTxLockVote vote;
vRecv >> vote;
uint256 nVoteHash = vote.GetHash();
pfrom->setAskFor.erase(nVoteHash);
// Ignore any InstantSend messages until masternode list is synced
if(!masternodeSync.IsMasternodeListSynced()) return;
{
LOCK(cs_instantsend);
auto ret = mapTxLockVotes.emplace(nVoteHash, vote);
if (!ret.second) return;
}
ProcessNewTxLockVote(pfrom, vote, connman);
2015-02-01 21:37:20 +01:00
2015-02-01 16:53:49 +01:00
return;
}
}
bool CInstantSend::ProcessTxLockRequest(const CTxLockRequest& txLockRequest, CConnman& connman)
{
LOCK(cs_main);
#ifdef ENABLE_WALLET
LOCK(pwalletMain ? &pwalletMain->cs_wallet : NULL);
#endif
LOCK2(mempool.cs, cs_instantsend);
2015-02-04 11:44:41 +01:00
uint256 txHash = txLockRequest.GetHash();
// Check to see if we conflict with existing completed lock
for (const auto& txin : txLockRequest.tx->vin) {
std::map<COutPoint, uint256>::iterator it = mapLockedOutpoints.find(txin.prevout);
if(it != mapLockedOutpoints.end() && it->second != txLockRequest.GetHash()) {
// Conflicting with complete lock, proceed to see if we should cancel them both
LogPrintf("CInstantSend::ProcessTxLockRequest -- WARNING: Found conflicting completed Transaction Lock, txid=%s, completed lock txid=%s\n",
txLockRequest.GetHash().ToString(), it->second.ToString());
}
2016-09-05 18:09:25 +02:00
}
2015-02-04 11:44:41 +01:00
// Check to see if there are votes for conflicting request,
// if so - do not fail, just warn user
for (const auto& txin : txLockRequest.tx->vin) {
std::map<COutPoint, std::set<uint256> >::iterator it = mapVotedOutpoints.find(txin.prevout);
if(it != mapVotedOutpoints.end()) {
for (const auto& hash : it->second) {
if(hash != txLockRequest.GetHash()) {
LogPrint("instantsend", "CInstantSend::ProcessTxLockRequest -- Double spend attempt! %s\n", txin.prevout.ToStringShort());
// do not fail here, let it go and see which one will get the votes to be locked
// TODO: notify zmq+script
}
}
2015-02-04 11:44:41 +01:00
}
}
if(!CreateTxLockCandidate(txLockRequest)) {
// smth is not right
LogPrintf("CInstantSend::ProcessTxLockRequest -- CreateTxLockCandidate failed, txid=%s\n", txHash.ToString());
return false;
}
LogPrintf("CInstantSend::ProcessTxLockRequest -- accepted, txid=%s\n", txHash.ToString());
// Masternodes will sometimes propagate votes before the transaction is known to the client.
// If this just happened - process orphan votes, lock inputs, resolve conflicting locks,
// update transaction status forcing external script/zmq notifications.
ProcessOrphanTxLockVotes();
std::map<uint256, CTxLockCandidate>::iterator itLockCandidate = mapTxLockCandidates.find(txHash);
TryToFinalizeLockCandidate(itLockCandidate->second);
return true;
}
bool CInstantSend::CreateTxLockCandidate(const CTxLockRequest& txLockRequest)
{
if(!txLockRequest.IsValid()) return false;
LOCK(cs_instantsend);
2015-02-04 22:09:50 +01:00
uint256 txHash = txLockRequest.GetHash();
std::map<uint256, CTxLockCandidate>::iterator itLockCandidate = mapTxLockCandidates.find(txHash);
if(itLockCandidate == mapTxLockCandidates.end()) {
LogPrintf("CInstantSend::CreateTxLockCandidate -- new, txid=%s\n", txHash.ToString());
CTxLockCandidate txLockCandidate(txLockRequest);
// all inputs should already be checked by txLockRequest.IsValid() above, just use them now
for(const auto& txin : txLockRequest.tx->vin) {
txLockCandidate.AddOutPointLock(txin.prevout);
}
mapTxLockCandidates.insert(std::make_pair(txHash, txLockCandidate));
} else if (!itLockCandidate->second.txLockRequest) {
// i.e. empty Transaction Lock Candidate was created earlier, let's update it with actual data
itLockCandidate->second.txLockRequest = txLockRequest;
if (itLockCandidate->second.IsTimedOut()) {
LogPrintf("CInstantSend::CreateTxLockCandidate -- timed out, txid=%s\n", txHash.ToString());
return false;
}
LogPrintf("CInstantSend::CreateTxLockCandidate -- update empty, txid=%s\n", txHash.ToString());
// all inputs should already be checked by txLockRequest.IsValid() above, just use them now
for(const auto& txin : txLockRequest.tx->vin) {
itLockCandidate->second.AddOutPointLock(txin.prevout);
}
2015-02-04 22:09:50 +01:00
} else {
LogPrint("instantsend", "CInstantSend::CreateTxLockCandidate -- seen, txid=%s\n", txHash.ToString());
2015-02-04 22:09:50 +01:00
}
return true;
}
void CInstantSend::CreateEmptyTxLockCandidate(const uint256& txHash)
{
if (mapTxLockCandidates.find(txHash) != mapTxLockCandidates.end())
return;
LogPrintf("CInstantSend::CreateEmptyTxLockCandidate -- new, txid=%s\n", txHash.ToString());
const CTxLockRequest txLockRequest = CTxLockRequest();
mapTxLockCandidates.insert(std::make_pair(txHash, CTxLockCandidate(txLockRequest)));
}
void CInstantSend::Vote(const uint256& txHash, CConnman& connman)
{
AssertLockHeld(cs_main);
#ifdef ENABLE_WALLET
LOCK(pwalletMain ? &pwalletMain->cs_wallet : NULL);
#endif
CTxLockRequest dummyRequest;
CTxLockCandidate txLockCandidate(dummyRequest);
{
LOCK(cs_instantsend);
auto itLockCandidate = mapTxLockCandidates.find(txHash);
if (itLockCandidate == mapTxLockCandidates.end()) return;
txLockCandidate = itLockCandidate->second;
Vote(txLockCandidate, connman);
}
// Let's see if our vote changed smth
LOCK2(mempool.cs, cs_instantsend);
TryToFinalizeLockCandidate(txLockCandidate);
}
void CInstantSend::Vote(CTxLockCandidate& txLockCandidate, CConnman& connman)
{
if(!fMasternodeMode) return;
if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return;
AssertLockHeld(cs_main);
AssertLockHeld(cs_instantsend);
2015-02-06 05:41:17 +01:00
uint256 txHash = txLockCandidate.GetHash();
// We should never vote on a Transaction Lock Request that was not (yet) accepted by the mempool
if(mapLockRequestAccepted.find(txHash) == mapLockRequestAccepted.end()) return;
// check if we need to vote on this candidate's outpoints,
// it's possible that we need to vote for several of them
for (auto& outpointLockPair : txLockCandidate.mapOutPointLocks) {
int nPrevoutHeight = GetUTXOHeight(outpointLockPair.first);
if(nPrevoutHeight == -1) {
LogPrint("instantsend", "CInstantSend::Vote -- Failed to find UTXO %s\n", outpointLockPair.first.ToStringShort());
return;
}
int nLockInputHeight = nPrevoutHeight + Params().GetConsensus().nInstantSendConfirmationsRequired - 2;
2015-02-06 05:41:17 +01:00
int nRank;
int nMinRequiredProtocol = std::max(MIN_INSTANTSEND_PROTO_VERSION, mnpayments.GetMinMasternodePaymentsProto());
if(!mnodeman.GetMasternodeRank(activeMasternode.outpoint, nRank, nLockInputHeight, nMinRequiredProtocol)) {
LogPrint("instantsend", "CInstantSend::Vote -- Can't calculate rank for masternode %s\n", activeMasternode.outpoint.ToStringShort());
continue;
}
int nSignaturesTotal = COutPointLock::SIGNATURES_TOTAL;
if(nRank > nSignaturesTotal) {
LogPrint("instantsend", "CInstantSend::Vote -- Masternode not in the top %d (%d)\n", nSignaturesTotal, nRank);
continue;
}
LogPrint("instantsend", "CInstantSend::Vote -- In the top %d (%d)\n", nSignaturesTotal, nRank);
std::map<COutPoint, std::set<uint256> >::iterator itVoted = mapVotedOutpoints.find(outpointLockPair.first);
// Check to see if we already voted for this outpoint,
// refuse to vote twice or to include the same outpoint in another tx
bool fAlreadyVoted = false;
if(itVoted != mapVotedOutpoints.end()) {
for (const auto& hash : itVoted->second) {
std::map<uint256, CTxLockCandidate>::iterator it2 = mapTxLockCandidates.find(hash);
if(it2->second.HasMasternodeVoted(outpointLockPair.first, activeMasternode.outpoint)) {
// we already voted for this outpoint to be included either in the same tx or in a competing one,
// skip it anyway
fAlreadyVoted = true;
LogPrintf("CInstantSend::Vote -- WARNING: We already voted for this outpoint, skipping: txHash=%s, outpoint=%s\n",
txHash.ToString(), outpointLockPair.first.ToStringShort());
break;
}
}
}
if(fAlreadyVoted) {
continue; // skip to the next outpoint
}
// we haven't voted for this outpoint yet, let's try to do this now
CTxLockVote vote(txHash, outpointLockPair.first, activeMasternode.outpoint);
if(!vote.Sign()) {
LogPrintf("CInstantSend::Vote -- Failed to sign consensus vote\n");
return;
}
if(!vote.CheckSignature()) {
LogPrintf("CInstantSend::Vote -- Signature invalid\n");
return;
}
// vote constructed sucessfully, let's store and relay it
uint256 nVoteHash = vote.GetHash();
mapTxLockVotes.insert(std::make_pair(nVoteHash, vote));
if(outpointLockPair.second.AddVote(vote)) {
LogPrintf("CInstantSend::Vote -- Vote created successfully, relaying: txHash=%s, outpoint=%s, vote=%s\n",
txHash.ToString(), outpointLockPair.first.ToStringShort(), nVoteHash.ToString());
if(itVoted == mapVotedOutpoints.end()) {
std::set<uint256> setHashes;
setHashes.insert(txHash);
mapVotedOutpoints.insert(std::make_pair(outpointLockPair.first, setHashes));
} else {
mapVotedOutpoints[outpointLockPair.first].insert(txHash);
if(mapVotedOutpoints[outpointLockPair.first].size() > 1) {
// it's ok to continue, just warn user
LogPrintf("CInstantSend::Vote -- WARNING: Vote conflicts with some existing votes: txHash=%s, outpoint=%s, vote=%s\n",
txHash.ToString(), outpointLockPair.first.ToStringShort(), nVoteHash.ToString());
}
}
vote.Relay(connman);
}
}
}
bool CInstantSend::ProcessNewTxLockVote(CNode* pfrom, const CTxLockVote& vote, CConnman& connman)
{
uint256 txHash = vote.GetTxHash();
uint256 nVoteHash = vote.GetHash();
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
if(!vote.IsValid(pfrom, connman)) {
// could be because of missing MN
LogPrint("instantsend", "CInstantSend::%s -- Vote is invalid, txid=%s\n", __func__, txHash.ToString());
return false;
}
// relay valid vote asap
vote.Relay(connman);
LOCK(cs_main);
#ifdef ENABLE_WALLET
LOCK(pwalletMain ? &pwalletMain->cs_wallet : NULL);
#endif
LOCK2(mempool.cs, cs_instantsend);
// Masternodes will sometimes propagate votes before the transaction is known to the client,
// will actually process only after the lock request itself has arrived
std::map<uint256, CTxLockCandidate>::iterator it = mapTxLockCandidates.find(txHash);
if(it == mapTxLockCandidates.end() || !it->second.txLockRequest) {
// no or empty tx lock candidate
if(it == mapTxLockCandidates.end()) {
// start timeout countdown after the very first vote
CreateEmptyTxLockCandidate(txHash);
}
bool fInserted = mapTxLockVotesOrphan.emplace(nVoteHash, vote).second;
LogPrint("instantsend", "CInstantSend::%s -- Orphan vote: txid=%s masternode=%s %s\n",
__func__, txHash.ToString(), vote.GetMasternodeOutpoint().ToStringShort(), fInserted ? "new" : "seen");
// This tracks those messages and allows only the same rate as of the rest of the network
// TODO: make sure this works good enough for multi-quorum
int nMasternodeOrphanExpireTime = GetTime() + 60*10; // keep time data for 10 minutes
auto itMnOV = mapMasternodeOrphanVotes.find(vote.GetMasternodeOutpoint());
if(itMnOV == mapMasternodeOrphanVotes.end()) {
mapMasternodeOrphanVotes.emplace(vote.GetMasternodeOutpoint(), nMasternodeOrphanExpireTime);
} else {
if(itMnOV->second > GetTime() && itMnOV->second > GetAverageMasternodeOrphanVoteTime()) {
LogPrint("instantsend", "CInstantSend::%s -- masternode is spamming orphan Transaction Lock Votes: txid=%s masternode=%s\n",
__func__, txHash.ToString(), vote.GetMasternodeOutpoint().ToStringShort());
// Misbehaving(pfrom->id, 1);
return false;
}
// not spamming, refresh
itMnOV->second = nMasternodeOrphanExpireTime;
}
return true;
}
// We have a valid (non-empty) tx lock candidate
CTxLockCandidate& txLockCandidate = it->second;
if (txLockCandidate.IsTimedOut()) {
LogPrint("instantsend", "CInstantSend::%s -- too late, Transaction Lock timed out, txid=%s\n", __func__, txHash.ToString());
return false;
}
LogPrint("instantsend", "CInstantSend::%s -- Transaction Lock Vote, txid=%s\n", __func__, txHash.ToString());
UpdateVotedOutpoints(vote, txLockCandidate);
if(!txLockCandidate.AddVote(vote)) {
// this should never happen
return false;
}
int nSignatures = txLockCandidate.CountVotes();
int nSignaturesMax = txLockCandidate.txLockRequest.GetMaxSignatures();
LogPrint("instantsend", "CInstantSend::%s -- Transaction Lock signatures count: %d/%d, vote hash=%s\n", __func__,
nSignatures, nSignaturesMax, nVoteHash.ToString());
TryToFinalizeLockCandidate(txLockCandidate);
return true;
}
bool CInstantSend::ProcessOrphanTxLockVote(const CTxLockVote& vote)
{
// cs_main, cs_wallet and cs_instantsend should be already locked
AssertLockHeld(cs_main);
#ifdef ENABLE_WALLET
if (pwalletMain)
AssertLockHeld(pwalletMain->cs_wallet);
#endif
AssertLockHeld(cs_instantsend);
uint256 txHash = vote.GetTxHash();
// We shouldn't process orphan votes without a valid tx lock candidate
std::map<uint256, CTxLockCandidate>::iterator it = mapTxLockCandidates.find(txHash);
if(it == mapTxLockCandidates.end() || !it->second.txLockRequest)
return false; // this shouldn never happen
CTxLockCandidate& txLockCandidate = it->second;
if (txLockCandidate.IsTimedOut()) {
LogPrint("instantsend", "CInstantSend::%s -- too late, Transaction Lock timed out, txid=%s\n", __func__, txHash.ToString());
return false;
}
LogPrint("instantsend", "CInstantSend::%s -- Transaction Lock Vote, txid=%s\n", __func__, txHash.ToString());
UpdateVotedOutpoints(vote, txLockCandidate);
if(!txLockCandidate.AddVote(vote)) {
// this should never happen
return false;
}
int nSignatures = txLockCandidate.CountVotes();
int nSignaturesMax = txLockCandidate.txLockRequest.GetMaxSignatures();
LogPrint("instantsend", "CInstantSend::%s -- Transaction Lock signatures count: %d/%d, vote hash=%s\n",
__func__, nSignatures, nSignaturesMax, vote.GetHash().ToString());
return true;
}
void CInstantSend::UpdateVotedOutpoints(const CTxLockVote& vote, CTxLockCandidate& txLockCandidate)
{
AssertLockHeld(cs_instantsend);
uint256 txHash = vote.GetTxHash();
std::map<COutPoint, std::set<uint256> >::iterator it1 = mapVotedOutpoints.find(vote.GetOutpoint());
if(it1 != mapVotedOutpoints.end()) {
for (const auto& hash : it1->second) {
if(hash != txHash) {
// same outpoint was already voted to be locked by another tx lock request,
// let's see if it was the same masternode who voted on this outpoint
// for another tx lock request
std::map<uint256, CTxLockCandidate>::iterator it2 = mapTxLockCandidates.find(hash);
if(it2 !=mapTxLockCandidates.end() && it2->second.HasMasternodeVoted(vote.GetOutpoint(), vote.GetMasternodeOutpoint())) {
// yes, it was the same masternode
LogPrintf("CInstantSend::%s -- masternode sent conflicting votes! %s\n", __func__, vote.GetMasternodeOutpoint().ToStringShort());
// mark both Lock Candidates as attacked, none of them should complete,
// or at least the new (current) one shouldn't even
// if the second one was already completed earlier
txLockCandidate.MarkOutpointAsAttacked(vote.GetOutpoint());
it2->second.MarkOutpointAsAttacked(vote.GetOutpoint());
// apply maximum PoSe ban score to this masternode i.e. PoSe-ban it instantly
mnodeman.PoSeBan(vote.GetMasternodeOutpoint());
// NOTE: This vote must be relayed further to let all other nodes know about such
// misbehaviour of this masternode. This way they should also be able to construct
// conflicting lock and PoSe-ban this masternode.
}
}
}
// store all votes, regardless of them being sent by malicious masternode or not
it1->second.insert(txHash);
} else {
mapVotedOutpoints.emplace(vote.GetOutpoint(), std::set<uint256>({txHash}));
}
}
void CInstantSend::ProcessOrphanTxLockVotes()
{
AssertLockHeld(cs_main);
AssertLockHeld(cs_instantsend);
std::map<uint256, CTxLockVote>::iterator it = mapTxLockVotesOrphan.begin();
while(it != mapTxLockVotesOrphan.end()) {
if(ProcessOrphanTxLockVote(it->second)) {
mapTxLockVotesOrphan.erase(it++);
} else {
++it;
}
}
}
void CInstantSend::TryToFinalizeLockCandidate(const CTxLockCandidate& txLockCandidate)
{
if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return;
AssertLockHeld(cs_main);
AssertLockHeld(cs_instantsend);
uint256 txHash = txLockCandidate.txLockRequest.tx->GetHash();
if(txLockCandidate.IsAllOutPointsReady() && !IsLockedInstantSendTransaction(txHash)) {
// we have enough votes now
LogPrint("instantsend", "CInstantSend::TryToFinalizeLockCandidate -- Transaction Lock is ready to complete, txid=%s\n", txHash.ToString());
if(ResolveConflicts(txLockCandidate)) {
LockTransactionInputs(txLockCandidate);
UpdateLockedTransaction(txLockCandidate);
}
}
}
void CInstantSend::UpdateLockedTransaction(const CTxLockCandidate& txLockCandidate)
{
// cs_main, cs_wallet and cs_instantsend should be already locked
AssertLockHeld(cs_main);
#ifdef ENABLE_WALLET
if (pwalletMain)
AssertLockHeld(pwalletMain->cs_wallet);
#endif
AssertLockHeld(cs_instantsend);
uint256 txHash = txLockCandidate.GetHash();
if(!IsLockedInstantSendTransaction(txHash)) return; // not a locked tx, do not update/notify
#ifdef ENABLE_WALLET
if(pwalletMain && pwalletMain->UpdatedTransaction(txHash)) {
// bumping this to update UI
nCompleteTXLocks++;
// notify an external script once threshold is reached
std::string strCmd = GetArg("-instantsendnotify", "");
if(!strCmd.empty()) {
boost::replace_all(strCmd, "%s", txHash.GetHex());
boost::thread t(runCommand, strCmd); // thread runs free
}
}
#endif
GetMainSignals().NotifyTransactionLock(*txLockCandidate.txLockRequest.tx);
LogPrint("instantsend", "CInstantSend::UpdateLockedTransaction -- done, txid=%s\n", txHash.ToString());
}
void CInstantSend::LockTransactionInputs(const CTxLockCandidate& txLockCandidate)
2015-02-05 17:48:57 +01:00
{
if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return;
2017-01-18 07:38:13 +01:00
LOCK(cs_instantsend);
2015-02-05 17:48:57 +01:00
uint256 txHash = txLockCandidate.GetHash();
if(!txLockCandidate.IsAllOutPointsReady()) return;
for (const auto& pair : txLockCandidate.mapOutPointLocks) {
mapLockedOutpoints.insert(std::make_pair(pair.first, txHash));
2015-02-05 17:48:57 +01:00
}
LogPrint("instantsend", "CInstantSend::LockTransactionInputs -- done, txid=%s\n", txHash.ToString());
2015-02-05 17:48:57 +01:00
}
bool CInstantSend::GetLockedOutPointTxHash(const COutPoint& outpoint, uint256& hashRet)
{
2017-01-18 07:38:13 +01:00
LOCK(cs_instantsend);
std::map<COutPoint, uint256>::iterator it = mapLockedOutpoints.find(outpoint);
if(it == mapLockedOutpoints.end()) return false;
hashRet = it->second;
return true;
}
bool CInstantSend::ResolveConflicts(const CTxLockCandidate& txLockCandidate)
{
AssertLockHeld(cs_main);
AssertLockHeld(cs_instantsend);
uint256 txHash = txLockCandidate.GetHash();
// make sure the lock is ready
if(!txLockCandidate.IsAllOutPointsReady()) return false;
AssertLockHeld(mempool.cs); // protect mempool.mapNextTx
for (const auto& txin : txLockCandidate.txLockRequest.tx->vin) {
uint256 hashConflicting;
if(GetLockedOutPointTxHash(txin.prevout, hashConflicting) && txHash != hashConflicting) {
// completed lock which conflicts with another completed one?
// this means that majority of MNs in the quorum for this specific tx input are malicious!
std::map<uint256, CTxLockCandidate>::iterator itLockCandidate = mapTxLockCandidates.find(txHash);
std::map<uint256, CTxLockCandidate>::iterator itLockCandidateConflicting = mapTxLockCandidates.find(hashConflicting);
if(itLockCandidate == mapTxLockCandidates.end() || itLockCandidateConflicting == mapTxLockCandidates.end()) {
// safety check, should never really happen
LogPrintf("CInstantSend::ResolveConflicts -- ERROR: Found conflicting completed Transaction Lock, but one of txLockCandidate-s is missing, txid=%s, conflicting txid=%s\n",
txHash.ToString(), hashConflicting.ToString());
return false;
}
LogPrintf("CInstantSend::ResolveConflicts -- WARNING: Found conflicting completed Transaction Lock, dropping both, txid=%s, conflicting txid=%s\n",
txHash.ToString(), hashConflicting.ToString());
CTxLockRequest txLockRequest = itLockCandidate->second.txLockRequest;
CTxLockRequest txLockRequestConflicting = itLockCandidateConflicting->second.txLockRequest;
itLockCandidate->second.SetConfirmedHeight(0); // expired
itLockCandidateConflicting->second.SetConfirmedHeight(0); // expired
CheckAndRemove(); // clean up
// AlreadyHave should still return "true" for both of them
2018-02-21 20:26:53 +01:00
mapLockRequestRejected.insert(std::make_pair(txHash, txLockRequest));
mapLockRequestRejected.insert(std::make_pair(hashConflicting, txLockRequestConflicting));
// TODO: clean up mapLockRequestRejected later somehow
// (not a big issue since we already PoSe ban malicious masternodes
// and they won't be able to spam)
// TODO: ban all malicious masternodes permanently, do not accept anything from them, ever
// TODO: notify zmq+script about this double-spend attempt
// and let merchant cancel/hold the order if it's not too late...
// can't do anything else, fallback to regular txes
return false;
} else if (mempool.mapNextTx.count(txin.prevout)) {
// check if it's in mempool
hashConflicting = mempool.mapNextTx.find(txin.prevout)->second->GetHash();
if(txHash == hashConflicting) continue; // matches current, not a conflict, skip to next txin
// conflicts with tx in mempool
LogPrintf("CInstantSend::ResolveConflicts -- ERROR: Failed to complete Transaction Lock, conflicts with mempool, txid=%s\n", txHash.ToString());
return false;
}
} // FOREACH
// No conflicts were found so far, check to see if it was already included in block
CTransactionRef txTmp;
uint256 hashBlock;
if(GetTransaction(txHash, txTmp, Params().GetConsensus(), hashBlock, true) && hashBlock != uint256()) {
LogPrint("instantsend", "CInstantSend::ResolveConflicts -- Done, %s is included in block %s\n", txHash.ToString(), hashBlock.ToString());
return true;
}
// Not in block yet, make sure all its inputs are still unspent
for (const auto& txin : txLockCandidate.txLockRequest.tx->vin) {
2017-09-26 16:33:46 +02:00
Coin coin;
if(!GetUTXOCoin(txin.prevout, coin)) {
// Not in UTXO anymore? A conflicting tx was mined while we were waiting for votes.
LogPrintf("CInstantSend::ResolveConflicts -- ERROR: Failed to find UTXO %s, can't complete Transaction Lock\n", txin.prevout.ToStringShort());
return false;
}
}
LogPrint("instantsend", "CInstantSend::ResolveConflicts -- Done, txid=%s\n", txHash.ToString());
return true;
}
int64_t CInstantSend::GetAverageMasternodeOrphanVoteTime()
{
2017-01-18 07:38:13 +01:00
LOCK(cs_instantsend);
// NOTE: should never actually call this function when mapMasternodeOrphanVotes is empty
if(mapMasternodeOrphanVotes.empty()) return 0;
int64_t total = 0;
for (const auto& pair : mapMasternodeOrphanVotes) {
total += pair.second;
}
return total / mapMasternodeOrphanVotes.size();
}
void CInstantSend::CheckAndRemove()
{
if(!masternodeSync.IsMasternodeListSynced()) return;
LOCK(cs_instantsend);
std::map<uint256, CTxLockCandidate>::iterator itLockCandidate = mapTxLockCandidates.begin();
// remove expired candidates
while(itLockCandidate != mapTxLockCandidates.end()) {
CTxLockCandidate &txLockCandidate = itLockCandidate->second;
uint256 txHash = txLockCandidate.GetHash();
if(txLockCandidate.IsExpired(nCachedBlockHeight)) {
LogPrintf("CInstantSend::CheckAndRemove -- Removing expired Transaction Lock Candidate: txid=%s\n", txHash.ToString());
for (const auto& pair : txLockCandidate.mapOutPointLocks) {
mapLockedOutpoints.erase(pair.first);
mapVotedOutpoints.erase(pair.first);
2015-02-04 21:20:13 +01:00
}
mapLockRequestAccepted.erase(txHash);
mapLockRequestRejected.erase(txHash);
mapTxLockCandidates.erase(itLockCandidate++);
} else {
++itLockCandidate;
}
}
2015-02-04 21:20:13 +01:00
// remove expired votes
std::map<uint256, CTxLockVote>::iterator itVote = mapTxLockVotes.begin();
while(itVote != mapTxLockVotes.end()) {
if(itVote->second.IsExpired(nCachedBlockHeight)) {
LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing expired vote: txid=%s masternode=%s\n",
itVote->second.GetTxHash().ToString(), itVote->second.GetMasternodeOutpoint().ToStringShort());
mapTxLockVotes.erase(itVote++);
} else {
++itVote;
}
}
// remove timed out orphan votes
std::map<uint256, CTxLockVote>::iterator itOrphanVote = mapTxLockVotesOrphan.begin();
while(itOrphanVote != mapTxLockVotesOrphan.end()) {
if(itOrphanVote->second.IsTimedOut()) {
LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing timed out orphan vote: txid=%s masternode=%s\n",
itOrphanVote->second.GetTxHash().ToString(), itOrphanVote->second.GetMasternodeOutpoint().ToStringShort());
mapTxLockVotes.erase(itOrphanVote->first);
mapTxLockVotesOrphan.erase(itOrphanVote++);
} else {
++itOrphanVote;
}
}
// remove invalid votes and votes for failed lock attempts
itVote = mapTxLockVotes.begin();
while(itVote != mapTxLockVotes.end()) {
if(itVote->second.IsFailed()) {
LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing vote for failed lock attempt: txid=%s masternode=%s\n",
itVote->second.GetTxHash().ToString(), itVote->second.GetMasternodeOutpoint().ToStringShort());
mapTxLockVotes.erase(itVote++);
} else {
++itVote;
}
}
// remove timed out masternode orphan votes (DOS protection)
std::map<COutPoint, int64_t>::iterator itMasternodeOrphan = mapMasternodeOrphanVotes.begin();
while(itMasternodeOrphan != mapMasternodeOrphanVotes.end()) {
if(itMasternodeOrphan->second < GetTime()) {
LogPrint("instantsend", "CInstantSend::CheckAndRemove -- Removing timed out orphan masternode vote: masternode=%s\n",
itMasternodeOrphan->first.ToStringShort());
mapMasternodeOrphanVotes.erase(itMasternodeOrphan++);
} else {
++itMasternodeOrphan;
}
}
LogPrintf("CInstantSend::CheckAndRemove -- %s\n", ToString());
}
bool CInstantSend::AlreadyHave(const uint256& hash)
{
LOCK(cs_instantsend);
return mapLockRequestAccepted.count(hash) ||
mapLockRequestRejected.count(hash) ||
mapTxLockVotes.count(hash);
}
void CInstantSend::AcceptLockRequest(const CTxLockRequest& txLockRequest)
{
2017-01-18 07:38:13 +01:00
LOCK(cs_instantsend);
2018-02-21 20:26:53 +01:00
mapLockRequestAccepted.insert(std::make_pair(txLockRequest.GetHash(), txLockRequest));
}
void CInstantSend::RejectLockRequest(const CTxLockRequest& txLockRequest)
{
LOCK(cs_instantsend);
2018-02-21 20:26:53 +01:00
mapLockRequestRejected.insert(std::make_pair(txLockRequest.GetHash(), txLockRequest));
}
bool CInstantSend::HasTxLockRequest(const uint256& txHash)
{
CTxLockRequest txLockRequestTmp;
return GetTxLockRequest(txHash, txLockRequestTmp);
}
bool CInstantSend::GetTxLockRequest(const uint256& txHash, CTxLockRequest& txLockRequestRet)
{
LOCK(cs_instantsend);
std::map<uint256, CTxLockCandidate>::iterator it = mapTxLockCandidates.find(txHash);
if(it == mapTxLockCandidates.end() || !it->second.txLockRequest) return false;
txLockRequestRet = it->second.txLockRequest;
return true;
}
bool CInstantSend::GetTxLockVote(const uint256& hash, CTxLockVote& txLockVoteRet)
{
LOCK(cs_instantsend);
std::map<uint256, CTxLockVote>::iterator it = mapTxLockVotes.find(hash);
if(it == mapTxLockVotes.end()) return false;
txLockVoteRet = it->second;
return true;
}
bool CInstantSend::IsInstantSendReadyToLock(const uint256& txHash)
{
if(!fEnableInstantSend || GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound() ||
!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return false;
LOCK(cs_instantsend);
// There must be a successfully verified lock request
// and all outputs must be locked (i.e. have enough signatures)
std::map<uint256, CTxLockCandidate>::iterator it = mapTxLockCandidates.find(txHash);
return it != mapTxLockCandidates.end() && it->second.IsAllOutPointsReady();
}
void CInstantSend::Clear()
{
LOCK(cs_instantsend);
mapLockRequestAccepted.clear();
mapLockRequestRejected.clear();
mapTxLockVotes.clear();
mapTxLockVotesOrphan.clear();
mapTxLockCandidates.clear();
mapVotedOutpoints.clear();
mapLockedOutpoints.clear();
mapMasternodeOrphanVotes.clear();
nCachedBlockHeight = 0;
}
bool CInstantSend::IsLockedInstantSendTransaction(const uint256& txHash)
{
if(!fEnableInstantSend || GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound() ||
!sporkManager.IsSporkActive(SPORK_3_INSTANTSEND_BLOCK_FILTERING)) return false;
LOCK(cs_instantsend);
// there must be a lock candidate
std::map<uint256, CTxLockCandidate>::iterator itLockCandidate = mapTxLockCandidates.find(txHash);
if(itLockCandidate == mapTxLockCandidates.end()) return false;
// which should have outpoints
if(itLockCandidate->second.mapOutPointLocks.empty()) return false;
// and all of these outputs must be included in mapLockedOutpoints with correct hash
for (const auto& pair : itLockCandidate->second.mapOutPointLocks) {
uint256 hashLocked;
if(!GetLockedOutPointTxHash(pair.first, hashLocked) || hashLocked != txHash) return false;
}
return true;
}
int CInstantSend::GetTransactionLockSignatures(const uint256& txHash)
{
2016-09-05 18:09:25 +02:00
if(!fEnableInstantSend) return -1;
if(GetfLargeWorkForkFound() || GetfLargeWorkInvalidChainFound()) return -2;
if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return -3;
2017-01-18 07:38:13 +01:00
LOCK(cs_instantsend);
std::map<uint256, CTxLockCandidate>::iterator itLockCandidate = mapTxLockCandidates.find(txHash);
if(itLockCandidate != mapTxLockCandidates.end()) {
return itLockCandidate->second.CountVotes();
}
return -1;
}
int CInstantSend::GetConfirmations(const uint256 &nTXHash)
{
return IsLockedInstantSendTransaction(nTXHash) ? nInstantSendDepth : 0;
}
bool CInstantSend::IsTxLockCandidateTimedOut(const uint256& txHash)
{
if(!fEnableInstantSend) return false;
2017-01-18 07:38:13 +01:00
LOCK(cs_instantsend);
std::map<uint256, CTxLockCandidate>::iterator itLockCandidate = mapTxLockCandidates.find(txHash);
if (itLockCandidate != mapTxLockCandidates.end()) {
return !itLockCandidate->second.IsAllOutPointsReady() &&
itLockCandidate->second.IsTimedOut();
}
return false;
}
void CInstantSend::Relay(const uint256& txHash, CConnman& connman)
{
LOCK(cs_instantsend);
std::map<uint256, CTxLockCandidate>::const_iterator itLockCandidate = mapTxLockCandidates.find(txHash);
if (itLockCandidate != mapTxLockCandidates.end()) {
itLockCandidate->second.Relay(connman);
}
}
void CInstantSend::UpdatedBlockTip(const CBlockIndex *pindex)
{
nCachedBlockHeight = pindex->nHeight;
}
void CInstantSend::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock)
{
// Update lock candidates and votes if corresponding tx confirmed
// or went from confirmed to 0-confirmed or conflicted.
if (tx.IsCoinBase()) return;
LOCK2(cs_main, cs_instantsend);
uint256 txHash = tx.GetHash();
// When tx is 0-confirmed or conflicted, posInBlock is SYNC_TRANSACTION_NOT_IN_BLOCK and nHeightNew should be set to -1
int nHeightNew = posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK ? -1 : pindex->nHeight;
LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d\n", txHash.ToString(), nHeightNew);
// Check lock candidates
std::map<uint256, CTxLockCandidate>::iterator itLockCandidate = mapTxLockCandidates.find(txHash);
if(itLockCandidate != mapTxLockCandidates.end()) {
LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d lock candidate updated\n",
txHash.ToString(), nHeightNew);
itLockCandidate->second.SetConfirmedHeight(nHeightNew);
// Loop through outpoint locks
for (const auto& pair : itLockCandidate->second.mapOutPointLocks) {
// Check corresponding lock votes
for (const auto& vote : pair.second.GetVotes()) {
uint256 nVoteHash = vote.GetHash();
LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated\n",
txHash.ToString(), nHeightNew, nVoteHash.ToString());
const auto& it = mapTxLockVotes.find(nVoteHash);
if(it != mapTxLockVotes.end()) {
it->second.SetConfirmedHeight(nHeightNew);
}
}
}
}
// check orphan votes
for (const auto& pair : mapTxLockVotesOrphan) {
if(pair.second.GetTxHash() == txHash) {
LogPrint("instantsend", "CInstantSend::SyncTransaction -- txid=%s nHeightNew=%d vote %s updated\n",
txHash.ToString(), nHeightNew, pair.first.ToString());
mapTxLockVotes[pair.first].SetConfirmedHeight(nHeightNew);
}
}
}
std::string CInstantSend::ToString() const
{
LOCK(cs_instantsend);
return strprintf("Lock Candidates: %llu, Votes %llu", mapTxLockCandidates.size(), mapTxLockVotes.size());
}
//
// CTxLockRequest
//
bool CTxLockRequest::IsValid() const
{
if(tx->vout.size() < 1) return false;
if(tx->vin.size() > WARN_MANY_INPUTS) {
LogPrint("instantsend", "CTxLockRequest::IsValid -- WARNING: Too many inputs: tx=%s", ToString());
}
AssertLockHeld(cs_main);
if(!CheckFinalTx(*tx)) {
LogPrint("instantsend", "CTxLockRequest::IsValid -- Transaction is not final: tx=%s", ToString());
return false;
}
CAmount nValueIn = 0;
int nInstantSendConfirmationsRequired = Params().GetConsensus().nInstantSendConfirmationsRequired;
for (const auto& txin : tx->vin) {
2017-09-26 16:33:46 +02:00
Coin coin;
2017-09-26 16:33:46 +02:00
if(!GetUTXOCoin(txin.prevout, coin)) {
LogPrint("instantsend", "CTxLockRequest::IsValid -- Failed to find UTXO %s\n", txin.prevout.ToStringShort());
return false;
}
2017-09-26 16:33:46 +02:00
int nTxAge = chainActive.Height() - coin.nHeight + 1;
// 1 less than the "send IX" gui requires, in case of a block propagating the network at the time
int nConfirmationsRequired = nInstantSendConfirmationsRequired - 1;
if(nTxAge < nConfirmationsRequired) {
LogPrint("instantsend", "CTxLockRequest::IsValid -- outpoint %s too new: nTxAge=%d, nConfirmationsRequired=%d, txid=%s\n",
txin.prevout.ToStringShort(), nTxAge, nConfirmationsRequired, GetHash().ToString());
return false;
}
2017-09-26 16:33:46 +02:00
nValueIn += coin.out.nValue;
}
if(nValueIn > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) {
LogPrint("instantsend", "CTxLockRequest::IsValid -- Transaction value too high: nValueIn=%d, tx=%s", nValueIn, ToString());
return false;
}
CAmount nValueOut = tx->GetValueOut();
if(nValueIn - nValueOut < GetMinFee()) {
LogPrint("instantsend", "CTxLockRequest::IsValid -- did not include enough fees in transaction: fees=%d, tx=%s", nValueOut - nValueIn, ToString());
return false;
}
return true;
}
CAmount CTxLockRequest::GetMinFee() const
{
CAmount nMinFee = MIN_FEE;
return std::max(nMinFee, CAmount(tx->vin.size() * nMinFee));
}
int CTxLockRequest::GetMaxSignatures() const
{
return tx->vin.size() * COutPointLock::SIGNATURES_TOTAL;
}
//
// CTxLockVote
//
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
bool CTxLockVote::IsValid(CNode* pnode, CConnman& connman) const
2015-02-01 16:53:49 +01:00
{
if(!mnodeman.Has(outpointMasternode)) {
LogPrint("instantsend", "CTxLockVote::IsValid -- Unknown masternode %s\n", outpointMasternode.ToStringShort());
Eliminate remaining uses of g_connman in Dash-specific code. (#1635) This monstrous change eliminates all remaining uses of g_connman global variable in Dash-specific code. Unlike previous changes eliminating g_connman use that were isolated to particular modules, this one covers multiple modules simultaneously because they are so interdependent that change in one module was quickly spreading to others. This is mostly invariant change that was done by * changing all functions using g_connman to use connman argument, * changing all functions calling these functions to use connman argument, * repeating previous step until there's nothing to change. After multiple iterations, this process converged to final result, producing code that is mostly equivalent to original one, but passing CConnman instance through arguments instead of global variable. The only exception to equivalence of resulting code is that I had to create overload of CMasternodeMan::CheckAndRemove() method without arguments that does nothing just for use in CFlatDB<CMasternodeMan>::Dump() and CFlatDB<CMasternodeMan>::Load() methods. Normal CMasternodeMan::CheckAndRemove() overload now has argument of CConnman& type and is used everywhere else. The normal overload has this code in the beginning: if(!masternodeSync.IsMasternodeListSynced()) return; Masternode list is not synced yet when we load "mncache.dat" file, and we save "mncache.dat" file on shutdown, so I presume that it's OK to use overload that does nothing in both cases. Signed-off-by: Oleg Girko <ol@infoserver.lv>
2017-09-19 16:51:38 +02:00
mnodeman.AskForMN(pnode, outpointMasternode, connman);
return false;
}
2017-09-26 16:33:46 +02:00
Coin coin;
if(!GetUTXOCoin(outpoint, coin)) {
LogPrint("instantsend", "CTxLockVote::IsValid -- Failed to find UTXO %s\n", outpoint.ToStringShort());
return false;
}
int nLockInputHeight = coin.nHeight + Params().GetConsensus().nInstantSendConfirmationsRequired - 2;
int nRank;
int nMinRequiredProtocol = std::max(MIN_INSTANTSEND_PROTO_VERSION, mnpayments.GetMinMasternodePaymentsProto());
if(!mnodeman.GetMasternodeRank(outpointMasternode, nRank, nLockInputHeight, nMinRequiredProtocol)) {
//can be caused by past versions trying to vote with an invalid protocol
LogPrint("instantsend", "CTxLockVote::IsValid -- Can't calculate rank for masternode %s\n", outpointMasternode.ToStringShort());
return false;
}
LogPrint("instantsend", "CTxLockVote::IsValid -- Masternode %s, rank=%d\n", outpointMasternode.ToStringShort(), nRank);
int nSignaturesTotal = COutPointLock::SIGNATURES_TOTAL;
if(nRank > nSignaturesTotal) {
LogPrint("instantsend", "CTxLockVote::IsValid -- Masternode %s is not in the top %d (%d), vote hash=%s\n",
outpointMasternode.ToStringShort(), nSignaturesTotal, nRank, GetHash().ToString());
return false;
}
if(!CheckSignature()) {
LogPrintf("CTxLockVote::IsValid -- Signature invalid\n");
return false;
}
return true;
2015-02-01 16:53:49 +01:00
}
uint256 CTxLockVote::GetHash() const
{
return SerializeHash(*this);
}
uint256 CTxLockVote::GetSignatureHash() const
{
return GetHash();
}
2015-02-01 16:53:49 +01:00
bool CTxLockVote::CheckSignature() const
{
std::string strError;
masternode_info_t infoMn;
if(!mnodeman.GetMasternodeInfo(outpointMasternode, infoMn)) {
LogPrintf("CTxLockVote::CheckSignature -- Unknown Masternode: masternode=%s\n", outpointMasternode.ToString());
return false;
}
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
uint256 hash = GetSignatureHash();
if (!CHashSigner::VerifyHash(hash, infoMn.pubKeyMasternode, vchMasternodeSignature, strError)) {
// could be a signature in old format
std::string strMessage = txHash.ToString() + outpoint.ToStringShort();
if(!CMessageSigner::VerifyMessage(infoMn.pubKeyMasternode, vchMasternodeSignature, strMessage, strError)) {
// nope, not in old format either
LogPrintf("CTxLockVote::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
return false;
}
}
} else {
std::string strMessage = txHash.ToString() + outpoint.ToStringShort();
if(!CMessageSigner::VerifyMessage(infoMn.pubKeyMasternode, vchMasternodeSignature, strMessage, strError)) {
LogPrintf("CTxLockVote::CheckSignature -- VerifyMessage() failed, error: %s\n", strError);
return false;
}
}
return true;
}
bool CTxLockVote::Sign()
{
std::string strError;
if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) {
uint256 hash = GetSignatureHash();
if(!CHashSigner::SignHash(hash, activeMasternode.keyMasternode, vchMasternodeSignature)) {
LogPrintf("CTxLockVote::Sign -- SignHash() failed\n");
return false;
}
if (!CHashSigner::VerifyHash(hash, activeMasternode.pubKeyMasternode, vchMasternodeSignature, strError)) {
LogPrintf("CTxLockVote::Sign -- VerifyHash() failed, error: %s\n", strError);
return false;
}
} else {
std::string strMessage = txHash.ToString() + outpoint.ToStringShort();
if(!CMessageSigner::SignMessage(strMessage, vchMasternodeSignature, activeMasternode.keyMasternode)) {
LogPrintf("CTxLockVote::Sign -- SignMessage() failed\n");
return false;
}
if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, vchMasternodeSignature, strMessage, strError)) {
LogPrintf("CTxLockVote::Sign -- VerifyMessage() failed, error: %s\n", strError);
return false;
}
}
return true;
}
void CTxLockVote::Relay(CConnman& connman) const
{
CInv inv(MSG_TXLOCK_VOTE, GetHash());
CTxLockRequest request;
if(instantsend.GetTxLockRequest(txHash, request))
connman.RelayInvFiltered(inv, *request.tx);
else
connman.RelayInv(inv);
}
bool CTxLockVote::IsExpired(int nHeight) const
{
// Locks and votes expire nInstantSendKeepLock blocks after the block corresponding tx was included into.
return (nConfirmedHeight != -1) && (nHeight - nConfirmedHeight > Params().GetConsensus().nInstantSendKeepLock);
}
bool CTxLockVote::IsTimedOut() const
{
return GetTime() - nTimeCreated > INSTANTSEND_LOCK_TIMEOUT_SECONDS;
}
bool CTxLockVote::IsFailed() const
{
return (GetTime() - nTimeCreated > INSTANTSEND_FAILED_TIMEOUT_SECONDS) && !instantsend.IsLockedInstantSendTransaction(GetTxHash());
}
//
// COutPointLock
//
bool COutPointLock::AddVote(const CTxLockVote& vote)
{
return mapMasternodeVotes.emplace(vote.GetMasternodeOutpoint(), vote).second;
}
std::vector<CTxLockVote> COutPointLock::GetVotes() const
{
std::vector<CTxLockVote> vRet;
for (const auto& pair : mapMasternodeVotes) {
vRet.push_back(pair.second);
}
return vRet;
}
bool COutPointLock::HasMasternodeVoted(const COutPoint& outpointMasternodeIn) const
{
return mapMasternodeVotes.count(outpointMasternodeIn);
}
void COutPointLock::Relay(CConnman& connman) const
{
for (const auto& pair : mapMasternodeVotes) {
pair.second.Relay(connman);
}
}
//
// CTxLockCandidate
//
void CTxLockCandidate::AddOutPointLock(const COutPoint& outpoint)
{
2018-02-21 20:26:53 +01:00
mapOutPointLocks.insert(std::make_pair(outpoint, COutPointLock(outpoint)));
}
void CTxLockCandidate::MarkOutpointAsAttacked(const COutPoint& outpoint)
{
std::map<COutPoint, COutPointLock>::iterator it = mapOutPointLocks.find(outpoint);
if(it != mapOutPointLocks.end())
it->second.MarkAsAttacked();
}
bool CTxLockCandidate::AddVote(const CTxLockVote& vote)
{
std::map<COutPoint, COutPointLock>::iterator it = mapOutPointLocks.find(vote.GetOutpoint());
if(it == mapOutPointLocks.end()) return false;
return it->second.AddVote(vote);
}
bool CTxLockCandidate::IsAllOutPointsReady() const
{
if(mapOutPointLocks.empty()) return false;
for (const auto& pair : mapOutPointLocks) {
if(!pair.second.IsReady()) return false;
}
return true;
}
bool CTxLockCandidate::HasMasternodeVoted(const COutPoint& outpointIn, const COutPoint& outpointMasternodeIn)
{
std::map<COutPoint, COutPointLock>::iterator it = mapOutPointLocks.find(outpointIn);
return it !=mapOutPointLocks.end() && it->second.HasMasternodeVoted(outpointMasternodeIn);
}
2015-02-04 22:09:50 +01:00
int CTxLockCandidate::CountVotes() const
{
// Note: do NOT use vote count to figure out if tx is locked, use IsAllOutPointsReady() instead
int nCountVotes = 0;
for (const auto& pair : mapOutPointLocks) {
nCountVotes += pair.second.CountVotes();
}
return nCountVotes;
}
2015-02-04 22:09:50 +01:00
bool CTxLockCandidate::IsExpired(int nHeight) const
{
// Locks and votes expire nInstantSendKeepLock blocks after the block corresponding tx was included into.
return (nConfirmedHeight != -1) && (nHeight - nConfirmedHeight > Params().GetConsensus().nInstantSendKeepLock);
}
bool CTxLockCandidate::IsTimedOut() const
{
return GetTime() - nTimeCreated > INSTANTSEND_LOCK_TIMEOUT_SECONDS;
}
void CTxLockCandidate::Relay(CConnman& connman) const
{
connman.RelayTransaction(*txLockRequest.tx);
for (const auto& pair : mapOutPointLocks) {
pair.second.Relay(connman);
}
}