Commit 58139f81 authored by Tom Bradshaw's avatar Tom Bradshaw

Masternode based block spam filtering.

parent 91c2f879
......@@ -142,7 +142,9 @@ BITCOIN_CORE_H = \
masternode-sync.h \
masternodeman.h \
masternodeconfig.h \
mn-pos/blockwitness.h \
mn-pos/kernel.h \
mn-pos/prooftracker.h \
mn-pos/stakepointer.h \
mn-pos/stakeminer.h \
mn-pos/stakevalidation.h \
......@@ -206,6 +208,7 @@ libbitcoin_server_a_SOURCES = \
merkleblock.cpp \
miner.cpp \
mn-pos/kernel.cpp \
mn-pos/prooftracker.cpp \
mn-pos/stakeminer.cpp \
mn-pos/stakepointer.cpp \
mn-pos/stakevalidation.cpp \
......
......@@ -30,7 +30,6 @@
#include "util.h"
#include "spork.h"
#include "utilmoneystr.h"
#include "mn-pos/stakevalidation.h"
#include <sstream>
......@@ -39,6 +38,8 @@
#include <boost/filesystem/fstream.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
#include <mn-pos/blockwitness.h>
#include <mn-pos/prooftracker.h>
#include <mn-pos/stakevalidation.h>
#include <mn-pos/stakepointer.h>
......@@ -58,6 +59,7 @@ CCriticalSection cs_main;
BlockMap mapBlockIndex;
CChain chainActive;
std::map<PointerHash, uint256> mapUsedStakePointers;
ProofTracker* g_proofTracker = new ProofTracker();
CBlockIndex *pindexBestHeader = NULL;
int64_t nTimeBestReceived = 0;
CWaitableCriticalSection csBestBlock;
......@@ -2226,7 +2228,7 @@ bool IsMasternodeOrSystemnodeReward(const CTransaction& tx, const COutPoint& out
return outpoint.n == MN_PMT_SLOT || outpoint.n == SN_PMT_SLOT;
}
bool CheckStake(const CBlockIndex* pindex, const CBlock& block)
bool CheckStake(const CBlockIndex* pindex, const CBlock& block, uint256& hashProofOfStake)
{
AssertLockHeld(cs_main);
//Coinbase has to be 0 value
......@@ -2249,7 +2251,7 @@ bool CheckStake(const CBlockIndex* pindex, const CBlock& block)
//cs_main is locked and mapblockindex checks for hashblock in CheckBlockProofPointer. Fine to access directly.
CBlockIndex* pindexFrom = mapBlockIndex.at(block.stakePointer.hashBlock);
return CheckProofOfStake(block, pindexFrom, outpointStakePointer);
return CheckProofOfStake(block, pindexFrom, outpointStakePointer, hashProofOfStake);
}
static int64_t nTimeVerify = 0;
......@@ -2271,7 +2273,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
return state.DoS(100, error("%s: Proof of Work block submitted after PoW end", __func__), REJECT_INVALID,
"pow-end");
if (!CheckStake(pindex, block))
uint256 hashProofOfStake;
if (!CheckStake(pindex, block, hashProofOfStake))
return state.DoS(100, error("%s: Block has invalid proof of stake", __func__), REJECT_INVALID, "pos-invalid");
} else if (block.IsProofOfStake()) {
......@@ -3506,6 +3509,20 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex,
return false;
}
if (pindex->nHeight > Params().PoSStartHeight()) {
uint256 hashProofOfStake;
if (!CheckStake(pindex, block, hashProofOfStake))
return error("%s: Proof of stake check failed", __func__);
// Check if this proof hash has been used to package other blocks
if (g_proofTracker->IsSuspicious(hashProofOfStake, pindex->GetBlockHash())) {
//Stake has been packaged into many different blocks. At this point we wait until enough masternodes have approved
//this block as on the main chain
return error("%s: hashProofOfStake has appeared in too many blocks, waiting for masternode approval for "
"block %s", __func__, pindex->GetBlockHash().GetHex());
}
}
int nHeight = pindex->nHeight;
// Write block to history file
......
......@@ -44,6 +44,7 @@ class CBlockIndex;
class CBlockTreeDB;
class CBloomFilter;
class CInv;
class ProofTracker;
class CScriptCheck;
class CValidationInterface;
class CValidationState;
......@@ -151,6 +152,7 @@ extern std::map<uint256, int64_t> mapRejectedBlocks;
typedef uint256 PointerHash;
extern std::map<PointerHash, uint256> mapUsedStakePointers; //pointer hash matched to blockhash that it is in
extern ProofTracker* g_proofTracker;
/** Best header we've seen so far (used for getheaders queries' starting points). */
extern CBlockIndex *pindexBestHeader;
......
......@@ -9,6 +9,9 @@
#include "util.h"
#include "sync.h"
#include "addrman.h"
#include "mn-pos/blockwitness.h"
#include "mn-pos/prooftracker.h"
#include <boost/lexical_cast.hpp>
// keep track of the scanning errors I've seen
......@@ -759,6 +762,9 @@ CMasternodePing::CMasternodePing()
blockHash = uint256();
sigTime = 0;
vchSig = std::vector<unsigned char>();
vchSigPrevBlocks = std::vector<unsigned char>();
vPrevBlockHash = std::vector<uint256>();
nVersion = 2;
}
CMasternodePing::CMasternodePing(const CTxIn& newVin)
......@@ -767,6 +773,15 @@ CMasternodePing::CMasternodePing(const CTxIn& newVin)
blockHash = chainActive[chainActive.Height() - 12]->GetBlockHash();
sigTime = GetAdjustedTime();
vchSig = std::vector<unsigned char>();
vchSigPrevBlocks = std::vector<unsigned char>();
vPrevBlockHash = std::vector<uint256>();
//Add previous 10 blocks
for (unsigned int i = 0; i < 10; i ++) {
vPrevBlockHash.emplace_back(chainActive[chainActive.Height() - i]->GetBlockHash());
}
nVersion = 2;
}
......@@ -788,6 +803,17 @@ bool CMasternodePing::Sign(const CKey& keyMasternode, const CPubKey& pubKeyMaste
return false;
}
uint256 hash = Hash(vPrevBlockHash.begin(), vPrevBlockHash.end());
if(!legacySigner.SignMessage(hash.GetHex(), errorMessage, vchSigPrevBlocks, keyMasternode)) {
LogPrintf("CMasternodePing::Sign() - Error signing previous blocks: %s\n", errorMessage);
return false;
}
if(!legacySigner.VerifyMessage(pubKeyMasternode, vchSigPrevBlocks, hash.GetHex(), errorMessage)) {
LogPrintf("CMasternodePing::Sign() - Error: %s\n", errorMessage);
return false;
}
return true;
}
......@@ -802,6 +828,17 @@ bool CMasternodePing::VerifySignature(const CPubKey& pubKeyMasternode, int &nDos
nDos = 33;
return false;
}
//Also check signature of previous blockhashes
if (nVersion > 1) {
uint256 hash = Hash(vPrevBlockHash.begin(), vPrevBlockHash.end());
if (!legacySigner.VerifyMessage(pubKeyMasternode, vchSigPrevBlocks, hash.GetHex(), errorMessage)) {
LogPrintf("CMasternodePing::VerifySignature - Got bad Masternode signature for previous blocks %s Error: %s\n", vin.ToString(), errorMessage);
nDos = 33;
return false;
}
}
return true;
}
......@@ -872,6 +909,12 @@ bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireEnabled, bool fChec
pmn->Check(true);
if(!pmn->IsEnabled()) return false;
if (this->nVersion > 1) {
for (const uint256& hashBlock : vPrevBlockHash) {
g_proofTracker->AddWitness(BlockWitness(pmn->vin, hashBlock));
}
}
LogPrint("masternode", "CMasternodePing::CheckAndUpdate - Masternode ping accepted, vin: %s\n", vin.ToString());
Relay();
......
......@@ -43,6 +43,12 @@ public:
uint256 blockHash;
int64_t sigTime; //mnb message times
std::vector<unsigned char> vchSig;
//Version 2 and above
int8_t nVersion;
std::vector<uint256> vPrevBlockHash; //10 previous blocks
std::vector<unsigned char> vchSigPrevBlocks;
//removed stop
CMasternodePing();
......@@ -57,6 +63,13 @@ public:
READWRITE(blockHash);
READWRITE(sigTime);
READWRITE(vchSig);
//New versioning signalled by flipping sign bit on sigTime
if (this->nVersion > 1) {
READWRITE(this->nVersion);
READWRITE(vPrevBlockHash);
READWRITE(vchSigPrevBlocks);
}
}
bool CheckAndUpdate(int& nDos, bool fRequireEnabled = true, bool fCheckSigTimeOnly = false) const;
......
......@@ -550,8 +550,14 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, std::string& strCommand, CData
}
}
else if (strCommand == "mnp") { //Masternode Ping
else if (strCommand == "mnp" || strCommand == "mnp_new") { //Masternode Ping
CMasternodePing mnp;
//coming in with old command, needs old serialization
if (strCommand == "mnp") {
mnp.nVersion = 1;
}
vRecv >> mnp;
LogPrint("masternode", "mnp - Masternode ping, vin: %s\n", mnp.vin.ToString());
......
#ifndef CROWNCOIN_BLOCKWITNESS_H
#define CROWNCOIN_BLOCKWITNESS_H
#include <primitives/transaction.h>
#include <vector>
class BlockWitness
{
public:
CTxIn m_vin;
uint256 m_hashBlock;
BlockWitness(const CTxIn& vin, const uint256& hashBlock) : m_vin(vin), m_hashBlock(hashBlock) {}
bool operator<(const BlockWitness& other) const
{
if (m_hashBlock != other.m_hashBlock)
return m_hashBlock < other.m_hashBlock;
return m_vin.prevout < other.m_vin.prevout;
}
};
#endif //CROWNCOIN_BLOCKWITNESS_H
#include "prooftracker.h"
#include "blockwitness.h"
#define REQUIRED_WITNESS_SIGS 6
#define STAKE_REPACKAGE_THRESHOLD 3
void ProofTracker::AddNewStake(const STAKEHASH& hashStake, const BLOCKHASH& hashBlock)
{
if (!m_mapStakes.count(hashStake))
m_mapStakes.emplace(std::make_pair(hashStake, std::set<BLOCKHASH>()));
m_mapStakes.at(hashStake).emplace(hashBlock);
}
void ProofTracker::AddWitness(const BlockWitness& witness)
{
if (!m_mapBlockWitness.count(witness.m_hashBlock))
m_mapBlockWitness.emplace(std::make_pair(witness.m_hashBlock, std::set<BlockWitness>()));
m_mapBlockWitness.at(witness.m_hashBlock).emplace(witness);
}
bool ProofTracker::IsSuspicious(const STAKEHASH& hashStake, const BLOCKHASH& hashBlock)
{
if (!m_mapStakes.count(hashStake)) {
AddNewStake(hashStake, hashBlock);
return false;
}
if (m_mapStakes.at(hashStake).size() >= STAKE_REPACKAGE_THRESHOLD) {
//If there are enough masternode that has signed off on this hash then it is not suspicious
if (!m_mapBlockWitness.count(hashBlock))
return true; //suspicious because no records of mn signing this block
// Factored out for legibility...
const auto& vWitness = m_mapBlockWitness.at(hashBlock);
bool isSuspicious = vWitness.size() < REQUIRED_WITNESS_SIGS;
if (isSuspicious)
return isSuspicious;
}
//Not suspicious, but still record knowledge of this stake so potential suspicious repackaging of this stake
//can be tracked
AddNewStake(hashStake, hashBlock);
return false;
}
\ No newline at end of file
#include <map>
#include <set>
class BlockWitness;
class uint256;
typedef uint256 BLOCKHASH;
typedef uint256 STAKEHASH;
class ProofTracker
{
private:
std::map<STAKEHASH, std::set<BLOCKHASH>> m_mapStakes;
std::map<BLOCKHASH, std::set<BlockWitness>> m_mapBlockWitness;
void AddNewStake(const STAKEHASH& hashStake, const BLOCKHASH& hashBlock);
public:
bool IsSuspicious(const STAKEHASH& hash, const BLOCKHASH& hashBlock);
void AddWitness(const BlockWitness& witness);
};
\ No newline at end of file
......@@ -16,7 +16,7 @@ bool CheckBlockSignature(const CBlock& block, const CPubKey& pubkeyMasternode)
}
// Check kernel hash target and coinstake signature
bool CheckProofOfStake(const CBlock& block, const CBlockIndex* prevBlock, const COutPoint& outpointStakePointer)
bool CheckProofOfStake(const CBlock& block, const CBlockIndex* prevBlock, const COutPoint& outpointStakePointer, uint256& hashProofOfStake)
{
const CTransaction tx = block.vtx[1];
if (!tx.IsCoinStake())
......@@ -53,5 +53,7 @@ bool CheckProofOfStake(const CBlock& block, const CBlockIndex* prevBlock, const
LogPrintf("%s : %s\n", __func__, kernel.ToString());
hashProofOfStake = kernel.GetStakeHash();
return kernel.IsValidProof(ArithToUint256(bnTarget));
}
\ No newline at end of file
......@@ -6,10 +6,11 @@ class CPubKey;
class CBlockIndex;
class COutPoint;
class CTransaction;
class uint256;
bool CheckBlockSignature(const CBlock& block, const CPubKey& pubkeyMasternode);
// Check kernel hash target and coinstake signature
bool CheckProofOfStake(const CBlock& block, const CBlockIndex* prevBlock, const COutPoint& outpointStakePointer);
bool CheckProofOfStake(const CBlock& block, const CBlockIndex* prevBlock, const COutPoint& outpointStakePointer, uint256& hashProofOfStake);
#endif //CROWNCORE_STAKEVALIDATION_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment