2016-02-02 16:28:56 +01:00
|
|
|
// Copyright (c) 2014-2016 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.
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2016-02-02 16:28:56 +01:00
|
|
|
#include "consensus/validation.h"
|
2014-12-09 02:17:57 +01:00
|
|
|
#include "sync.h"
|
|
|
|
#include "net.h"
|
|
|
|
#include "key.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "base58.h"
|
|
|
|
#include "protocol.h"
|
|
|
|
#include "instantx.h"
|
|
|
|
#include "activemasternode.h"
|
|
|
|
#include "darksend.h"
|
2016-01-24 20:05:31 +01:00
|
|
|
#include "masternode-sync.h"
|
|
|
|
#include "masternodeman.h"
|
2015-02-09 20:28:29 +01:00
|
|
|
#include "spork.h"
|
2014-12-09 02:17:57 +01:00
|
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace boost;
|
2014-12-31 03:54:00 +01:00
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
std::map<uint256, CTransaction> mapTxLockReq;
|
|
|
|
std::map<uint256, CTransaction> mapTxLockReqRejected;
|
2015-02-06 20:07:22 +01:00
|
|
|
std::map<uint256, CConsensusVote> mapTxLockVote;
|
2014-12-09 02:17:57 +01:00
|
|
|
std::map<uint256, CTransactionLock> mapTxLocks;
|
2015-02-04 21:20:13 +01:00
|
|
|
std::map<COutPoint, uint256> mapLockedInputs;
|
2015-02-02 18:33:52 +01:00
|
|
|
std::map<uint256, int64_t> mapUnknownVotes; //track votes with no tx for DOS
|
2015-02-01 21:04:20 +01:00
|
|
|
int nCompleteTXLocks;
|
2014-12-09 02:17:57 +01:00
|
|
|
|
|
|
|
//txlock - Locks transaction
|
|
|
|
//
|
|
|
|
//step 1.) Broadcast intention to lock transaction inputs, "txlreg", CTransaction
|
2015-06-04 20:54:33 +02:00
|
|
|
//step 2.) Top INSTANTX_SIGNATURES_TOTAL masternodes, open connect to top 1 masternode.
|
|
|
|
// Send "txvote", CTransaction, Signature, Approve
|
|
|
|
//step 3.) Top 1 masternode, waits for INSTANTX_SIGNATURES_REQUIRED messages. Upon success, sends "txlock'
|
2014-12-09 02:17:57 +01:00
|
|
|
|
|
|
|
void ProcessMessageInstantX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
|
|
|
|
{
|
2015-01-18 16:28:16 +01:00
|
|
|
if(fLiteMode) return; //disable all darksend/masternode related functionality
|
2015-02-09 20:28:29 +01:00
|
|
|
if(!IsSporkActive(SPORK_2_INSTANTX)) return;
|
2015-08-07 06:48:55 +02:00
|
|
|
if(!masternodeSync.IsBlockchainSynced()) return;
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2016-02-17 23:18:57 +01:00
|
|
|
if (strCommand == NetMsgType::IX)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2015-07-10 00:08:26 +02:00
|
|
|
//LogPrintf("ProcessMessageInstantX::ix\n");
|
2014-12-09 02:17:57 +01:00
|
|
|
CDataStream vMsg(vRecv);
|
|
|
|
CTransaction tx;
|
|
|
|
vRecv >> tx;
|
|
|
|
|
|
|
|
CInv inv(MSG_TXLOCK_REQUEST, tx.GetHash());
|
|
|
|
pfrom->AddInventoryKnown(inv);
|
|
|
|
|
2016-03-21 21:23:45 +01:00
|
|
|
// have we seen it already?
|
|
|
|
if(mapTxLockReq.count(inv.hash) || mapTxLockReqRejected.count(inv.hash)) return;
|
|
|
|
// is it a valid one?
|
|
|
|
if(!IsIXTXValid(tx)) return;
|
2015-02-04 11:44:41 +01:00
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
BOOST_FOREACH(const CTxOut o, tx.vout){
|
2015-07-12 23:02:39 +02:00
|
|
|
// IX supports normal scripts and unspendable scripts (used in DS collateral and Budget collateral).
|
|
|
|
// TODO: Look into other script types that are normal and can be included
|
|
|
|
if(!o.scriptPubKey.IsNormalPaymentScript() && !o.scriptPubKey.IsUnspendable()){
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("ProcessMessageInstantX::ix - Invalid Script %s\n", tx.ToString());
|
2014-12-09 02:17:57 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-04 22:59:19 +01:00
|
|
|
int nBlockHeight = CreateNewLock(tx);
|
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
bool fMissingInputs = false;
|
|
|
|
CValidationState state;
|
2014-12-31 03:54:00 +01:00
|
|
|
|
2015-08-05 00:49:34 +02:00
|
|
|
bool fAccepted = false;
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2015-08-05 00:49:34 +02:00
|
|
|
LOCK(cs_main);
|
|
|
|
fAccepted = AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs);
|
|
|
|
}
|
|
|
|
if (fAccepted)
|
|
|
|
{
|
|
|
|
RelayInv(inv);
|
2015-02-06 20:07:22 +01:00
|
|
|
|
2015-02-04 22:59:19 +01:00
|
|
|
DoConsensusVote(tx, nBlockHeight);
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2015-02-02 15:36:00 +01:00
|
|
|
mapTxLockReq.insert(make_pair(tx.GetHash(), tx));
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2015-07-10 00:08:26 +02:00
|
|
|
LogPrintf("ProcessMessageInstantX::ix - Transaction Lock Request: %s %s : accepted %s\n",
|
2016-03-04 06:58:53 +01:00
|
|
|
pfrom->addr.ToString(), pfrom->cleanSubVer,
|
|
|
|
tx.GetHash().ToString()
|
2014-12-09 02:17:57 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
} else {
|
2015-02-03 18:17:30 +01:00
|
|
|
mapTxLockReqRejected.insert(make_pair(tx.GetHash(), tx));
|
2014-12-09 02:17:57 +01:00
|
|
|
|
|
|
|
// can we get the conflicting transaction as proof?
|
|
|
|
|
2015-07-10 00:08:26 +02:00
|
|
|
LogPrintf("ProcessMessageInstantX::ix - Transaction Lock Request: %s %s : rejected %s\n",
|
2016-03-04 06:58:53 +01:00
|
|
|
pfrom->addr.ToString(), pfrom->cleanSubVer,
|
|
|
|
tx.GetHash().ToString()
|
2014-12-09 02:17:57 +01:00
|
|
|
);
|
|
|
|
|
2015-02-04 21:20:13 +01:00
|
|
|
BOOST_FOREACH(const CTxIn& in, tx.vin){
|
|
|
|
if(!mapLockedInputs.count(in.prevout)){
|
|
|
|
mapLockedInputs.insert(make_pair(in.prevout, tx.GetHash()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-03 18:17:30 +01:00
|
|
|
// resolve conflicts
|
2016-03-23 15:49:35 +01:00
|
|
|
if (IsLockedIXTransaction(tx.GetHash()) && !CheckForConflictingLocks(tx)){
|
|
|
|
LogPrintf("ProcessMessageInstantX::ix - Found Existing Complete IX Lock\n");
|
|
|
|
|
|
|
|
//reprocess the last 15 blocks
|
|
|
|
ReprocessBlocks(15);
|
|
|
|
mapTxLockReq.insert(make_pair(tx.GetHash(), tx));
|
2015-02-03 23:40:00 +01:00
|
|
|
}
|
2015-02-03 18:17:30 +01:00
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-12-31 03:54:00 +01:00
|
|
|
}
|
2016-02-17 23:18:57 +01:00
|
|
|
else if (strCommand == NetMsgType::IXLOCKVOTE) //InstantX Lock Consensus Votes
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
|
|
|
CConsensusVote ctx;
|
|
|
|
vRecv >> ctx;
|
|
|
|
|
2015-02-01 16:53:49 +01:00
|
|
|
CInv inv(MSG_TXLOCK_VOTE, ctx.GetHash());
|
2014-12-09 02:17:57 +01:00
|
|
|
pfrom->AddInventoryKnown(inv);
|
|
|
|
|
2015-02-01 21:37:20 +01:00
|
|
|
if(mapTxLockVote.count(ctx.GetHash())){
|
2015-02-01 16:53:49 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2015-02-06 20:07:22 +01:00
|
|
|
mapTxLockVote.insert(make_pair(ctx.GetHash(), ctx));
|
2014-12-31 03:54:00 +01:00
|
|
|
|
2015-08-07 05:07:40 +02:00
|
|
|
if(ProcessConsensusVote(pfrom, ctx)){
|
2015-02-02 15:36:00 +01:00
|
|
|
//Spam/Dos protection
|
2015-02-02 18:33:52 +01:00
|
|
|
/*
|
|
|
|
Masternodes will sometimes propagate votes before the transaction is known to the client.
|
|
|
|
This tracks those messages and allows it at the same rate of the rest of the network, if
|
|
|
|
a peer violates it, it will simply be ignored
|
|
|
|
*/
|
2015-02-02 15:36:00 +01:00
|
|
|
if(!mapTxLockReq.count(ctx.txHash) && !mapTxLockReqRejected.count(ctx.txHash)){
|
|
|
|
if(!mapUnknownVotes.count(ctx.vinMasternode.prevout.hash)){
|
|
|
|
mapUnknownVotes[ctx.vinMasternode.prevout.hash] = GetTime()+(60*10);
|
|
|
|
}
|
|
|
|
|
2015-02-02 18:33:52 +01:00
|
|
|
if(mapUnknownVotes[ctx.vinMasternode.prevout.hash] > GetTime() &&
|
|
|
|
mapUnknownVotes[ctx.vinMasternode.prevout.hash] - GetAverageVoteTime() > 60*10){
|
2015-07-10 00:08:26 +02:00
|
|
|
LogPrintf("ProcessMessageInstantX::ix - masternode is spamming transaction votes: %s %s\n",
|
2016-03-04 06:58:53 +01:00
|
|
|
ctx.vinMasternode.ToString(),
|
|
|
|
ctx.txHash.ToString()
|
2015-02-02 18:33:52 +01:00
|
|
|
);
|
|
|
|
return;
|
2015-02-02 15:36:00 +01:00
|
|
|
} else {
|
|
|
|
mapUnknownVotes[ctx.vinMasternode.prevout.hash] = GetTime()+(60*10);
|
|
|
|
}
|
|
|
|
}
|
2015-08-05 00:49:34 +02:00
|
|
|
RelayInv(inv);
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
2015-02-01 21:37:20 +01:00
|
|
|
|
2015-02-01 16:53:49 +01:00
|
|
|
return;
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-04 11:44:41 +01:00
|
|
|
bool IsIXTXValid(const CTransaction& txCollateral){
|
|
|
|
if(txCollateral.vout.size() < 1) return false;
|
2016-03-21 21:23:45 +01:00
|
|
|
|
|
|
|
if(!CheckFinalTx(txCollateral)) {
|
|
|
|
LogPrint("instantx", "IsIXTXValid - Transaction is not final - %s\n", txCollateral.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
2015-02-04 11:44:41 +01:00
|
|
|
|
|
|
|
int64_t nValueIn = 0;
|
|
|
|
int64_t nValueOut = 0;
|
|
|
|
bool missingTx = false;
|
|
|
|
|
|
|
|
BOOST_FOREACH(const CTxOut o, txCollateral.vout)
|
|
|
|
nValueOut += o.nValue;
|
|
|
|
|
|
|
|
BOOST_FOREACH(const CTxIn i, txCollateral.vin){
|
|
|
|
CTransaction tx2;
|
|
|
|
uint256 hash;
|
2016-02-02 16:28:56 +01:00
|
|
|
if(GetTransaction(i.prevout.hash, tx2, Params().GetConsensus(), hash, true)){
|
2015-02-04 11:44:41 +01:00
|
|
|
if(tx2.vout.size() > i.prevout.n) {
|
|
|
|
nValueIn += tx2.vout[i.prevout.n].nValue;
|
|
|
|
}
|
|
|
|
} else{
|
|
|
|
missingTx = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 15:47:21 +01:00
|
|
|
if(nValueOut > GetSporkValue(SPORK_5_MAX_VALUE)*COIN){
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "IsIXTXValid - Transaction value too high - %s\n", txCollateral.ToString());
|
2015-02-11 15:47:21 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-02-04 11:44:41 +01:00
|
|
|
if(missingTx){
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "IsIXTXValid - Unknown inputs in IX transaction - %s\n", txCollateral.ToString());
|
2015-02-08 04:04:08 +01:00
|
|
|
/*
|
|
|
|
This happens sometimes for an unknown reason, so we'll return that it's a valid transaction.
|
|
|
|
If someone submits an invalid transaction it will be rejected by the network anyway and this isn't
|
|
|
|
very common, but we don't want to block IX just because the client can't figure out the fee.
|
|
|
|
*/
|
|
|
|
return true;
|
2015-02-04 11:44:41 +01:00
|
|
|
}
|
|
|
|
|
2016-01-24 20:05:31 +01:00
|
|
|
if(nValueIn-nValueOut < CENT) {
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "IsIXTXValid - did not include enough fees in transaction %d\n%s\n", nValueOut-nValueIn, txCollateral.ToString());
|
2015-02-04 11:44:41 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-02-04 22:59:19 +01:00
|
|
|
int64_t CreateNewLock(CTransaction tx)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2015-02-04 22:59:19 +01:00
|
|
|
|
|
|
|
int64_t nTxAge = 0;
|
|
|
|
BOOST_REVERSE_FOREACH(CTxIn i, tx.vin){
|
|
|
|
nTxAge = GetInputAge(i);
|
2015-08-04 19:42:05 +02:00
|
|
|
if(nTxAge < 5) //1 less than the "send IX" gui requires, incase of a block propagating the network at the time
|
2015-02-04 22:59:19 +01:00
|
|
|
{
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("CreateNewLock - Transaction not found / too new: %d / %s\n", nTxAge, tx.GetHash().ToString());
|
2015-02-04 22:59:19 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2015-02-04 22:09:50 +01:00
|
|
|
/*
|
2015-02-04 22:59:19 +01:00
|
|
|
Use a blockheight newer than the input.
|
|
|
|
This prevents attackers from using transaction mallibility to predict which masternodes
|
|
|
|
they'll use.
|
2015-02-04 22:09:50 +01:00
|
|
|
*/
|
2016-03-02 21:57:24 +01:00
|
|
|
int nBlockHeight = 0;
|
|
|
|
{
|
|
|
|
LOCK(cs_main);
|
|
|
|
CBlockIndex* tip = chainActive.Tip();
|
|
|
|
if(tip) nBlockHeight = tip->nHeight - nTxAge + 4;
|
|
|
|
else return 0;
|
|
|
|
}
|
2015-02-04 22:09:50 +01:00
|
|
|
|
|
|
|
if (!mapTxLocks.count(tx.GetHash())){
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("CreateNewLock - New Transaction Lock %s !\n", tx.GetHash().ToString());
|
2015-02-04 22:09:50 +01:00
|
|
|
|
|
|
|
CTransactionLock newLock;
|
|
|
|
newLock.nBlockHeight = nBlockHeight;
|
2015-07-07 14:47:22 +02:00
|
|
|
newLock.nExpiration = GetTime()+(60*60); //locks expire after 60 minutes (24 confirmations)
|
2015-02-05 16:52:02 +01:00
|
|
|
newLock.nTimeout = GetTime()+(60*5);
|
2015-02-04 22:09:50 +01:00
|
|
|
newLock.txHash = tx.GetHash();
|
|
|
|
mapTxLocks.insert(make_pair(tx.GetHash(), newLock));
|
|
|
|
} else {
|
|
|
|
mapTxLocks[tx.GetHash()].nBlockHeight = nBlockHeight;
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "CreateNewLock - Transaction Lock Exists %s !\n", tx.GetHash().ToString());
|
2015-02-04 22:09:50 +01:00
|
|
|
}
|
|
|
|
|
2015-03-13 10:28:20 +01:00
|
|
|
|
|
|
|
|
2015-02-04 22:59:19 +01:00
|
|
|
return nBlockHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we need to vote on this transaction
|
|
|
|
void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight)
|
|
|
|
{
|
|
|
|
if(!fMasterNode) return;
|
|
|
|
|
2015-02-23 21:01:21 +01:00
|
|
|
int n = mnodeman.GetMasternodeRank(activeMasternode.vin, nBlockHeight, MIN_INSTANTX_PROTO_VERSION);
|
2015-02-06 05:41:17 +01:00
|
|
|
|
|
|
|
if(n == -1)
|
|
|
|
{
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "InstantX::DoConsensusVote - Unknown Masternode %s\n", activeMasternode.vin.ToString());
|
2015-02-06 05:41:17 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(n > INSTANTX_SIGNATURES_TOTAL)
|
|
|
|
{
|
2015-06-24 23:11:45 +02:00
|
|
|
LogPrint("instantx", "InstantX::DoConsensusVote - Masternode not in the top %d (%d)\n", INSTANTX_SIGNATURES_TOTAL, n);
|
2015-02-06 05:41:17 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-02-04 22:59:19 +01:00
|
|
|
/*
|
|
|
|
nBlockHeight calculated from the transaction is the authoritive source
|
|
|
|
*/
|
|
|
|
|
2015-06-24 23:11:45 +02:00
|
|
|
LogPrint("instantx", "InstantX::DoConsensusVote - In the top %d (%d)\n", INSTANTX_SIGNATURES_TOTAL, n);
|
2015-02-06 05:41:17 +01:00
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
CConsensusVote ctx;
|
2014-12-06 20:41:53 +01:00
|
|
|
ctx.vinMasternode = activeMasternode.vin;
|
2015-02-02 13:24:04 +01:00
|
|
|
ctx.txHash = tx.GetHash();
|
2014-12-31 03:54:00 +01:00
|
|
|
ctx.nBlockHeight = nBlockHeight;
|
2014-12-09 02:17:57 +01:00
|
|
|
if(!ctx.Sign()){
|
|
|
|
LogPrintf("InstantX::DoConsensusVote - Failed to sign consensus vote\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(!ctx.SignatureValid()) {
|
|
|
|
LogPrintf("InstantX::DoConsensusVote - Signature invalid\n");
|
|
|
|
return;
|
|
|
|
}
|
2015-02-02 13:01:06 +01:00
|
|
|
|
2015-02-06 20:07:22 +01:00
|
|
|
mapTxLockVote[ctx.GetHash()] = ctx;
|
|
|
|
|
|
|
|
CInv inv(MSG_TXLOCK_VOTE, ctx.GetHash());
|
2015-07-08 02:37:23 +02:00
|
|
|
RelayInv(inv);
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//received a consensus vote
|
2015-08-07 05:07:40 +02:00
|
|
|
bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2015-02-23 21:01:21 +01:00
|
|
|
int n = mnodeman.GetMasternodeRank(ctx.vinMasternode, ctx.nBlockHeight, MIN_INSTANTX_PROTO_VERSION);
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2015-02-25 12:54:03 +01:00
|
|
|
CMasternode* pmn = mnodeman.Find(ctx.vinMasternode);
|
|
|
|
if(pmn != NULL)
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "InstantX::ProcessConsensusVote - Masternode ADDR %s %d\n", pmn->addr.ToString(), n);
|
2015-02-06 16:54:39 +01:00
|
|
|
|
2014-12-31 03:54:00 +01:00
|
|
|
if(n == -1)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2015-02-06 17:03:50 +01:00
|
|
|
//can be caused by past versions trying to vote with an invalid protocol
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "InstantX::ProcessConsensusVote - Unknown Masternode %s\n", ctx.vinMasternode.ToString());
|
2015-08-07 05:07:40 +02:00
|
|
|
mnodeman.AskForMN(pnode, ctx.vinMasternode);
|
2015-02-02 15:36:00 +01:00
|
|
|
return false;
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
|
|
|
|
2015-02-05 18:56:11 +01:00
|
|
|
if(n > INSTANTX_SIGNATURES_TOTAL)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "InstantX::ProcessConsensusVote - Masternode not in the top %d (%d) - %s\n", INSTANTX_SIGNATURES_TOTAL, n, ctx.GetHash().ToString());
|
2015-02-02 15:36:00 +01:00
|
|
|
return false;
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!ctx.SignatureValid()) {
|
|
|
|
LogPrintf("InstantX::ProcessConsensusVote - Signature invalid\n");
|
2015-08-07 05:07:40 +02:00
|
|
|
// don't ban, it could just be a non-synced masternode
|
|
|
|
mnodeman.AskForMN(pnode, ctx.vinMasternode);
|
2015-02-02 15:36:00 +01:00
|
|
|
return false;
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
|
|
|
|
2015-02-02 13:24:04 +01:00
|
|
|
if (!mapTxLocks.count(ctx.txHash)){
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("InstantX::ProcessConsensusVote - New Transaction Lock %s !\n", ctx.txHash.ToString());
|
2015-02-01 21:04:20 +01:00
|
|
|
|
2015-02-01 16:53:49 +01:00
|
|
|
CTransactionLock newLock;
|
2015-02-04 22:09:50 +01:00
|
|
|
newLock.nBlockHeight = 0;
|
2015-02-01 21:04:20 +01:00
|
|
|
newLock.nExpiration = GetTime()+(60*60);
|
2015-02-05 16:52:02 +01:00
|
|
|
newLock.nTimeout = GetTime()+(60*5);
|
2015-02-02 13:24:04 +01:00
|
|
|
newLock.txHash = ctx.txHash;
|
|
|
|
mapTxLocks.insert(make_pair(ctx.txHash, newLock));
|
2015-06-24 23:11:45 +02:00
|
|
|
} else
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Exists %s !\n", ctx.txHash.ToString());
|
2015-02-01 16:53:49 +01:00
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
//compile consessus vote
|
2015-02-02 13:24:04 +01:00
|
|
|
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(ctx.txHash);
|
2015-02-01 16:53:49 +01:00
|
|
|
if (i != mapTxLocks.end()){
|
|
|
|
(*i).second.AddSignature(ctx);
|
2015-02-03 23:40:00 +01:00
|
|
|
|
2015-02-05 19:29:13 +01:00
|
|
|
#ifdef ENABLE_WALLET
|
|
|
|
if(pwalletMain){
|
|
|
|
//when we get back signatures, we'll count them as requests. Otherwise the client will think it didn't propagate.
|
|
|
|
if(pwalletMain->mapRequestCount.count(ctx.txHash))
|
|
|
|
pwalletMain->mapRequestCount[ctx.txHash]++;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-03-21 21:23:45 +01:00
|
|
|
int nSignatures = (*i).second.CountSignatures();
|
|
|
|
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Votes %d - %s !\n", nSignatures, ctx.GetHash().ToString());
|
2015-02-03 23:40:00 +01:00
|
|
|
|
2016-03-21 21:23:45 +01:00
|
|
|
if(nSignatures >= INSTANTX_SIGNATURES_REQUIRED){
|
|
|
|
LogPrint("instantx", "InstantX::ProcessConsensusVote - Transaction Lock Is Complete %s !\n", ctx.txHash.ToString());
|
2015-02-03 18:17:30 +01:00
|
|
|
|
2015-02-05 17:48:57 +01:00
|
|
|
CTransaction& tx = mapTxLockReq[ctx.txHash];
|
|
|
|
if(!CheckForConflictingLocks(tx)){
|
|
|
|
|
2015-02-04 22:19:18 +01:00
|
|
|
#ifdef ENABLE_WALLET
|
2015-02-05 17:48:57 +01:00
|
|
|
if(pwalletMain){
|
2016-03-21 21:23:45 +01:00
|
|
|
if(pwalletMain->UpdatedTransaction(ctx.txHash)){
|
|
|
|
// bumping this to update UI
|
2015-02-05 17:48:57 +01:00
|
|
|
nCompleteTXLocks++;
|
|
|
|
}
|
2015-02-04 22:19:18 +01:00
|
|
|
}
|
|
|
|
#endif
|
2015-02-03 18:17:30 +01:00
|
|
|
|
2015-02-05 17:48:57 +01:00
|
|
|
if(mapTxLockReq.count(ctx.txHash)){
|
|
|
|
BOOST_FOREACH(const CTxIn& in, tx.vin){
|
|
|
|
if(!mapLockedInputs.count(in.prevout)){
|
|
|
|
mapLockedInputs.insert(make_pair(in.prevout, ctx.txHash));
|
|
|
|
}
|
2015-02-04 21:20:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-05 17:48:57 +01:00
|
|
|
// resolve conflicts
|
2015-02-04 02:19:54 +01:00
|
|
|
|
2015-02-05 17:48:57 +01:00
|
|
|
//if this tx lock was rejected, we need to remove the conflicting blocks
|
2016-03-21 21:23:45 +01:00
|
|
|
if(mapTxLockReqRejected.count(ctx.txHash)){
|
2015-07-31 20:53:40 +02:00
|
|
|
//reprocess the last 15 blocks
|
2015-08-03 01:08:37 +02:00
|
|
|
ReprocessBlocks(15);
|
2015-02-05 17:48:57 +01:00
|
|
|
}
|
2015-02-03 23:40:00 +01:00
|
|
|
}
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
2015-02-02 15:36:00 +01:00
|
|
|
return true;
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
|
|
|
|
2015-02-01 16:53:49 +01:00
|
|
|
|
2015-02-02 15:36:00 +01:00
|
|
|
return false;
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
|
|
|
|
2015-02-05 17:48:57 +01:00
|
|
|
bool CheckForConflictingLocks(CTransaction& tx)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
It's possible (very unlikely though) to get 2 conflicting transaction locks approved by the network.
|
|
|
|
In that case, they will cancel each other out.
|
|
|
|
|
|
|
|
Blocks could have been rejected during this time, which is OK. After they cancel out, the client will
|
|
|
|
rescan the blocks and find they're acceptable and then take the chain with the most work.
|
|
|
|
*/
|
|
|
|
BOOST_FOREACH(const CTxIn& in, tx.vin){
|
|
|
|
if(mapLockedInputs.count(in.prevout)){
|
|
|
|
if(mapLockedInputs[in.prevout] != tx.GetHash()){
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("InstantX::CheckForConflictingLocks - found two complete conflicting locks - removing both. %s %s", tx.GetHash().ToString(), mapLockedInputs[in.prevout].ToString());
|
2015-02-05 17:48:57 +01:00
|
|
|
if(mapTxLocks.count(tx.GetHash())) mapTxLocks[tx.GetHash()].nExpiration = GetTime();
|
|
|
|
if(mapTxLocks.count(mapLockedInputs[in.prevout])) mapTxLocks[mapLockedInputs[in.prevout]].nExpiration = GetTime();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2015-02-02 18:33:52 +01:00
|
|
|
|
|
|
|
int64_t GetAverageVoteTime()
|
|
|
|
{
|
|
|
|
std::map<uint256, int64_t>::iterator it = mapUnknownVotes.begin();
|
|
|
|
int64_t total = 0;
|
|
|
|
int64_t count = 0;
|
|
|
|
|
|
|
|
while(it != mapUnknownVotes.end()) {
|
|
|
|
total+= it->second;
|
|
|
|
count++;
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return total / count;
|
|
|
|
}
|
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
void CleanTransactionLocksList()
|
|
|
|
{
|
|
|
|
std::map<uint256, CTransactionLock>::iterator it = mapTxLocks.begin();
|
2014-12-31 03:54:00 +01:00
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
while(it != mapTxLocks.end()) {
|
2015-02-01 21:04:20 +01:00
|
|
|
if(GetTime() > it->second.nExpiration){ //keep them for an hour
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("Removing old transaction lock %s\n", it->second.txHash.ToString());
|
2015-02-04 21:20:13 +01:00
|
|
|
|
|
|
|
if(mapTxLockReq.count(it->second.txHash)){
|
|
|
|
CTransaction& tx = mapTxLockReq[it->second.txHash];
|
2015-02-04 21:25:12 +01:00
|
|
|
|
2015-02-04 21:20:13 +01:00
|
|
|
BOOST_FOREACH(const CTxIn& in, tx.vin)
|
|
|
|
mapLockedInputs.erase(in.prevout);
|
|
|
|
|
|
|
|
mapTxLockReq.erase(it->second.txHash);
|
|
|
|
mapTxLockReqRejected.erase(it->second.txHash);
|
2015-02-06 20:58:03 +01:00
|
|
|
|
|
|
|
BOOST_FOREACH(CConsensusVote& v, it->second.vecConsensusVotes)
|
|
|
|
mapTxLockVote.erase(v.GetHash());
|
2015-02-04 21:20:13 +01:00
|
|
|
}
|
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
mapTxLocks.erase(it++);
|
|
|
|
} else {
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-23 15:49:35 +01:00
|
|
|
bool IsLockedIXTransaction(uint256 txHash) {
|
|
|
|
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(txHash);
|
|
|
|
return i != mapTxLocks.end() && (*i).second.CountSignatures() >= INSTANTX_SIGNATURES_REQUIRED;
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetTransactionLockSignatures(uint256 txHash)
|
|
|
|
{
|
|
|
|
if(fLargeWorkForkFound || fLargeWorkInvalidChainFound) return -2;
|
|
|
|
if(!IsSporkActive(SPORK_2_INSTANTX)) return -3;
|
|
|
|
if(!fEnableInstantX) return -1;
|
|
|
|
|
|
|
|
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(txHash);
|
|
|
|
if (i != mapTxLocks.end()){
|
|
|
|
return (*i).second.CountSignatures();
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsTransactionLockTimedOut(uint256 txHash)
|
|
|
|
{
|
|
|
|
if(!fEnableInstantX) return 0;
|
|
|
|
|
|
|
|
std::map<uint256, CTransactionLock>::iterator i = mapTxLocks.find(txHash);
|
|
|
|
if (i != mapTxLocks.end()){
|
|
|
|
return GetTime() > (*i).second.nTimeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-02-01 16:53:49 +01:00
|
|
|
uint256 CConsensusVote::GetHash() const
|
|
|
|
{
|
2016-02-02 16:28:56 +01:00
|
|
|
return ArithToUint256(UintToArith256(vinMasternode.prevout.hash) + vinMasternode.prevout.n + UintToArith256(txHash));
|
2015-02-01 16:53:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-09 02:17:57 +01:00
|
|
|
bool CConsensusVote::SignatureValid()
|
|
|
|
{
|
|
|
|
std::string errorMessage;
|
2015-02-02 15:36:00 +01:00
|
|
|
std::string strMessage = txHash.ToString().c_str() + boost::lexical_cast<std::string>(nBlockHeight);
|
2016-03-04 06:58:53 +01:00
|
|
|
//LogPrintf("verify strMessage %s \n", strMessage);
|
2014-12-31 03:54:00 +01:00
|
|
|
|
2015-02-25 12:54:03 +01:00
|
|
|
CMasternode* pmn = mnodeman.Find(vinMasternode);
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2015-02-25 12:54:03 +01:00
|
|
|
if(pmn == NULL)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("InstantX::CConsensusVote::SignatureValid() - Unknown Masternode %s\n", vinMasternode.ToString());
|
2014-12-09 02:17:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-02-25 12:54:03 +01:00
|
|
|
if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) {
|
2014-12-09 02:17:57 +01:00
|
|
|
LogPrintf("InstantX::CConsensusVote::SignatureValid() - Verify message failed\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CConsensusVote::Sign()
|
|
|
|
{
|
|
|
|
std::string errorMessage;
|
|
|
|
|
|
|
|
CKey key2;
|
|
|
|
CPubKey pubkey2;
|
2015-02-02 15:36:00 +01:00
|
|
|
std::string strMessage = txHash.ToString().c_str() + boost::lexical_cast<std::string>(nBlockHeight);
|
2016-03-04 06:58:53 +01:00
|
|
|
//LogPrintf("signing strMessage %s \n", strMessage);
|
|
|
|
//LogPrintf("signing privkey %s \n", strMasterNodePrivKey);
|
2014-12-09 02:17:57 +01:00
|
|
|
|
|
|
|
if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, key2, pubkey2))
|
|
|
|
{
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("CConsensusVote::Sign() - ERROR: Invalid masternodeprivkey: '%s'\n", errorMessage);
|
2015-01-18 16:28:16 +01:00
|
|
|
return false;
|
2014-12-09 02:17:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchMasterNodeSignature, key2)) {
|
2015-06-24 23:27:56 +02:00
|
|
|
LogPrintf("CConsensusVote::Sign() - Sign message failed");
|
2014-12-09 02:17:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubkey2, vchMasterNodeSignature, strMessage, errorMessage)) {
|
2015-06-24 23:27:56 +02:00
|
|
|
LogPrintf("CConsensusVote::Sign() - Verify message failed");
|
2014-12-09 02:17:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CTransactionLock::SignaturesValid()
|
|
|
|
{
|
|
|
|
|
|
|
|
BOOST_FOREACH(CConsensusVote vote, vecConsensusVotes)
|
|
|
|
{
|
2015-02-23 21:01:21 +01:00
|
|
|
int n = mnodeman.GetMasternodeRank(vote.vinMasternode, vote.nBlockHeight, MIN_INSTANTX_PROTO_VERSION);
|
2014-12-09 02:17:57 +01:00
|
|
|
|
2014-12-31 03:54:00 +01:00
|
|
|
if(n == -1)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2016-03-04 06:58:53 +01:00
|
|
|
LogPrintf("CTransactionLock::SignaturesValid() - Unknown Masternode %s\n", vote.vinMasternode.ToString());
|
2014-12-09 02:17:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-02-05 18:56:11 +01:00
|
|
|
if(n > INSTANTX_SIGNATURES_TOTAL)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
2015-06-24 23:27:56 +02:00
|
|
|
LogPrintf("CTransactionLock::SignaturesValid() - Masternode not in the top %s\n", INSTANTX_SIGNATURES_TOTAL);
|
2014-12-09 02:17:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!vote.SignatureValid()){
|
2015-06-24 23:27:56 +02:00
|
|
|
LogPrintf("CTransactionLock::SignaturesValid() - Signature not valid\n");
|
2014-12-09 02:17:57 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-02-06 20:07:22 +01:00
|
|
|
void CTransactionLock::AddSignature(CConsensusVote& cv)
|
2014-12-09 02:17:57 +01:00
|
|
|
{
|
|
|
|
vecConsensusVotes.push_back(cv);
|
|
|
|
}
|
|
|
|
|
|
|
|
int CTransactionLock::CountSignatures()
|
|
|
|
{
|
2015-02-04 22:09:50 +01:00
|
|
|
/*
|
|
|
|
Only count signatures where the BlockHeight matches the transaction's blockheight.
|
|
|
|
The votes have no proof it's the correct blockheight
|
|
|
|
*/
|
|
|
|
|
2015-02-05 05:05:36 +01:00
|
|
|
if(nBlockHeight == 0) return -1;
|
2015-02-04 22:09:50 +01:00
|
|
|
|
|
|
|
int n = 0;
|
|
|
|
BOOST_FOREACH(CConsensusVote v, vecConsensusVotes){
|
|
|
|
if(v.nBlockHeight == nBlockHeight){
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return n;
|
2014-12-06 20:41:53 +01:00
|
|
|
}
|