841 lines
30 KiB
C++
841 lines
30 KiB
C++
// Copyright (c) 2014-2015 The Dash developers
|
|
// Copyright (c) 2015-2018 The PIVX developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include "masternode.h"
|
|
#include "addrman.h"
|
|
#include "masternodeman.h"
|
|
#include "obfuscation.h"
|
|
#include "sync.h"
|
|
#include "util.h"
|
|
|
|
// keep track of the scanning errors I've seen
|
|
map<uint256, int> mapSeenMasternodeScanningErrors;
|
|
// cache block hashes as we calculate them
|
|
std::map<int64_t, uint256> mapCacheBlockHashes;
|
|
|
|
//Get the last hash that matches the modulus given. Processed in reverse order
|
|
bool GetBlockHash(uint256& hash, int nBlockHeight)
|
|
{
|
|
if (chainActive.Tip() == NULL) return false;
|
|
|
|
if (nBlockHeight == 0)
|
|
nBlockHeight = chainActive.Tip()->nHeight;
|
|
|
|
if (mapCacheBlockHashes.count(nBlockHeight)) {
|
|
hash = mapCacheBlockHashes[nBlockHeight];
|
|
return true;
|
|
}
|
|
|
|
const CBlockIndex* BlockLastSolved = chainActive.Tip();
|
|
const CBlockIndex* BlockReading = chainActive.Tip();
|
|
|
|
if (BlockLastSolved == NULL || BlockLastSolved->nHeight == 0 || chainActive.Tip()->nHeight + 1 < nBlockHeight) return false;
|
|
|
|
int nBlocksAgo = 0;
|
|
if (nBlockHeight > 0) nBlocksAgo = (chainActive.Tip()->nHeight + 1) - nBlockHeight;
|
|
assert(nBlocksAgo >= 0);
|
|
|
|
int n = 0;
|
|
for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) {
|
|
if (n >= nBlocksAgo) {
|
|
hash = BlockReading->GetBlockHash();
|
|
mapCacheBlockHashes[nBlockHeight] = hash;
|
|
return true;
|
|
}
|
|
n++;
|
|
|
|
if (BlockReading->pprev == NULL) {
|
|
assert(BlockReading);
|
|
break;
|
|
}
|
|
BlockReading = BlockReading->pprev;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
CMasternode::CMasternode()
|
|
{
|
|
LOCK(cs);
|
|
vin = CTxIn();
|
|
addr = CService();
|
|
pubKeyCollateralAddress = CPubKey();
|
|
pubKeyMasternode = CPubKey();
|
|
sig = std::vector<unsigned char>();
|
|
activeState = MASTERNODE_ENABLED;
|
|
sigTime = GetAdjustedTime();
|
|
lastPing = CMasternodePing();
|
|
cacheInputAge = 0;
|
|
cacheInputAgeBlock = 0;
|
|
unitTest = false;
|
|
allowFreeTx = true;
|
|
nActiveState = MASTERNODE_ENABLED,
|
|
protocolVersion = PROTOCOL_VERSION;
|
|
nLastDsq = 0;
|
|
nScanningErrorCount = 0;
|
|
nLastScanningErrorBlockHeight = 0;
|
|
lastTimeChecked = 0;
|
|
nLastDsee = 0; // temporary, do not save. Remove after migration to v12
|
|
nLastDseep = 0; // temporary, do not save. Remove after migration to v12
|
|
}
|
|
|
|
CMasternode::CMasternode(const CMasternode& other)
|
|
{
|
|
LOCK(cs);
|
|
vin = other.vin;
|
|
addr = other.addr;
|
|
pubKeyCollateralAddress = other.pubKeyCollateralAddress;
|
|
pubKeyMasternode = other.pubKeyMasternode;
|
|
sig = other.sig;
|
|
activeState = other.activeState;
|
|
sigTime = other.sigTime;
|
|
lastPing = other.lastPing;
|
|
cacheInputAge = other.cacheInputAge;
|
|
cacheInputAgeBlock = other.cacheInputAgeBlock;
|
|
unitTest = other.unitTest;
|
|
allowFreeTx = other.allowFreeTx;
|
|
nActiveState = MASTERNODE_ENABLED,
|
|
protocolVersion = other.protocolVersion;
|
|
nLastDsq = other.nLastDsq;
|
|
nScanningErrorCount = other.nScanningErrorCount;
|
|
nLastScanningErrorBlockHeight = other.nLastScanningErrorBlockHeight;
|
|
lastTimeChecked = 0;
|
|
nLastDsee = other.nLastDsee; // temporary, do not save. Remove after migration to v12
|
|
nLastDseep = other.nLastDseep; // temporary, do not save. Remove after migration to v12
|
|
}
|
|
|
|
CMasternode::CMasternode(const CMasternodeBroadcast& mnb)
|
|
{
|
|
LOCK(cs);
|
|
vin = mnb.vin;
|
|
addr = mnb.addr;
|
|
pubKeyCollateralAddress = mnb.pubKeyCollateralAddress;
|
|
pubKeyMasternode = mnb.pubKeyMasternode;
|
|
sig = mnb.sig;
|
|
activeState = MASTERNODE_ENABLED;
|
|
sigTime = mnb.sigTime;
|
|
lastPing = mnb.lastPing;
|
|
cacheInputAge = 0;
|
|
cacheInputAgeBlock = 0;
|
|
unitTest = false;
|
|
allowFreeTx = true;
|
|
nActiveState = MASTERNODE_ENABLED,
|
|
protocolVersion = mnb.protocolVersion;
|
|
nLastDsq = mnb.nLastDsq;
|
|
nScanningErrorCount = 0;
|
|
nLastScanningErrorBlockHeight = 0;
|
|
lastTimeChecked = 0;
|
|
nLastDsee = 0; // temporary, do not save. Remove after migration to v12
|
|
nLastDseep = 0; // temporary, do not save. Remove after migration to v12
|
|
}
|
|
|
|
//
|
|
// When a new masternode broadcast is sent, update our information
|
|
//
|
|
bool CMasternode::UpdateFromNewBroadcast(CMasternodeBroadcast& mnb)
|
|
{
|
|
if (mnb.sigTime > sigTime) {
|
|
pubKeyMasternode = mnb.pubKeyMasternode;
|
|
pubKeyCollateralAddress = mnb.pubKeyCollateralAddress;
|
|
sigTime = mnb.sigTime;
|
|
sig = mnb.sig;
|
|
protocolVersion = mnb.protocolVersion;
|
|
addr = mnb.addr;
|
|
lastTimeChecked = 0;
|
|
int nDoS = 0;
|
|
if (mnb.lastPing == CMasternodePing() || (mnb.lastPing != CMasternodePing() && mnb.lastPing.CheckAndUpdate(nDoS, false))) {
|
|
lastPing = mnb.lastPing;
|
|
mnodeman.mapSeenMasternodePing.insert(make_pair(lastPing.GetHash(), lastPing));
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Deterministically calculate a given "score" for a Masternode depending on how close it's hash is to
|
|
// the proof of work for that block. The further away they are the better, the furthest will win the election
|
|
// and get paid this block
|
|
//
|
|
uint256 CMasternode::CalculateScore(int mod, int64_t nBlockHeight)
|
|
{
|
|
if (chainActive.Tip() == NULL) return 0;
|
|
|
|
uint256 hash = 0;
|
|
uint256 aux = vin.prevout.hash + vin.prevout.n;
|
|
|
|
if (!GetBlockHash(hash, nBlockHeight)) {
|
|
LogPrint("masternode","CalculateScore ERROR - nHeight %d - Returned 0\n", nBlockHeight);
|
|
return 0;
|
|
}
|
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << hash;
|
|
uint256 hash2 = ss.GetHash();
|
|
|
|
CHashWriter ss2(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss2 << hash;
|
|
ss2 << aux;
|
|
uint256 hash3 = ss2.GetHash();
|
|
|
|
uint256 r = (hash3 > hash2 ? hash3 - hash2 : hash2 - hash3);
|
|
|
|
return r;
|
|
}
|
|
|
|
void CMasternode::Check(bool forceCheck)
|
|
{
|
|
if (ShutdownRequested()) return;
|
|
|
|
if (!forceCheck && (GetTime() - lastTimeChecked < MASTERNODE_CHECK_SECONDS)) return;
|
|
lastTimeChecked = GetTime();
|
|
|
|
|
|
//once spent, stop doing the checks
|
|
if (activeState == MASTERNODE_VIN_SPENT) return;
|
|
|
|
|
|
if (!IsPingedWithin(MASTERNODE_REMOVAL_SECONDS)) {
|
|
activeState = MASTERNODE_REMOVE;
|
|
return;
|
|
}
|
|
|
|
if (!IsPingedWithin(MASTERNODE_EXPIRATION_SECONDS)) {
|
|
activeState = MASTERNODE_EXPIRED;
|
|
return;
|
|
}
|
|
|
|
if(lastPing.sigTime - sigTime < MASTERNODE_MIN_MNP_SECONDS){
|
|
activeState = MASTERNODE_PRE_ENABLED;
|
|
return;
|
|
}
|
|
|
|
if (!unitTest) {
|
|
CValidationState state;
|
|
CMutableTransaction tx = CMutableTransaction();
|
|
CTxOut vout = CTxOut(9999.99 * COIN, obfuScationPool.collateralPubKey);
|
|
tx.vin.push_back(vin);
|
|
tx.vout.push_back(vout);
|
|
|
|
{
|
|
TRY_LOCK(cs_main, lockMain);
|
|
if (!lockMain) return;
|
|
|
|
if (!AcceptableInputs(mempool, state, CTransaction(tx), false, NULL)) {
|
|
activeState = MASTERNODE_VIN_SPENT;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
activeState = MASTERNODE_ENABLED; // OK
|
|
}
|
|
|
|
int64_t CMasternode::SecondsSincePayment()
|
|
{
|
|
CScript pubkeyScript;
|
|
pubkeyScript = GetScriptForDestination(pubKeyCollateralAddress.GetID());
|
|
|
|
int64_t sec = (GetAdjustedTime() - GetLastPaid());
|
|
int64_t month = 60 * 60 * 24 * 30;
|
|
if (sec < month) return sec; //if it's less than 30 days, give seconds
|
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << vin;
|
|
ss << sigTime;
|
|
uint256 hash = ss.GetHash();
|
|
|
|
// return some deterministic value for unknown/unpaid but force it to be more than 30 days old
|
|
return month + hash.GetCompact(false);
|
|
}
|
|
|
|
int64_t CMasternode::GetLastPaid()
|
|
{
|
|
CBlockIndex* pindexPrev = chainActive.Tip();
|
|
if (pindexPrev == NULL) return false;
|
|
|
|
CScript mnpayee;
|
|
mnpayee = GetScriptForDestination(pubKeyCollateralAddress.GetID());
|
|
|
|
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
|
|
ss << vin;
|
|
ss << sigTime;
|
|
uint256 hash = ss.GetHash();
|
|
|
|
// use a deterministic offset to break a tie -- 2.5 minutes
|
|
int64_t nOffset = hash.GetCompact(false) % 150;
|
|
|
|
if (chainActive.Tip() == NULL) return false;
|
|
|
|
const CBlockIndex* BlockReading = chainActive.Tip();
|
|
|
|
int nMnCount = mnodeman.CountEnabled() * 1.25;
|
|
int n = 0;
|
|
for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) {
|
|
if (n >= nMnCount) {
|
|
return 0;
|
|
}
|
|
n++;
|
|
|
|
if (masternodePayments.mapMasternodeBlocks.count(BlockReading->nHeight)) {
|
|
/*
|
|
Search for this payee, with at least 2 votes. This will aid in consensus allowing the network
|
|
to converge on the same payees quickly, then keep the same schedule.
|
|
*/
|
|
if (masternodePayments.mapMasternodeBlocks[BlockReading->nHeight].HasPayeeWithVotes(mnpayee, 2)) {
|
|
return BlockReading->nTime + nOffset;
|
|
}
|
|
}
|
|
|
|
if (BlockReading->pprev == NULL) {
|
|
assert(BlockReading);
|
|
break;
|
|
}
|
|
BlockReading = BlockReading->pprev;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
std::string CMasternode::GetStatus()
|
|
{
|
|
switch (nActiveState) {
|
|
case CMasternode::MASTERNODE_PRE_ENABLED:
|
|
return "PRE_ENABLED";
|
|
case CMasternode::MASTERNODE_ENABLED:
|
|
return "ENABLED";
|
|
case CMasternode::MASTERNODE_EXPIRED:
|
|
return "EXPIRED";
|
|
case CMasternode::MASTERNODE_OUTPOINT_SPENT:
|
|
return "OUTPOINT_SPENT";
|
|
case CMasternode::MASTERNODE_REMOVE:
|
|
return "REMOVE";
|
|
case CMasternode::MASTERNODE_WATCHDOG_EXPIRED:
|
|
return "WATCHDOG_EXPIRED";
|
|
case CMasternode::MASTERNODE_POSE_BAN:
|
|
return "POSE_BAN";
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
bool CMasternode::IsValidNetAddr()
|
|
{
|
|
// TODO: regtest is fine with any addresses for now,
|
|
// should probably be a bit smarter if one day we start to implement tests for this
|
|
return Params().NetworkID() == CBaseChainParams::REGTEST ||
|
|
(IsReachable(addr) && addr.IsRoutable());
|
|
}
|
|
|
|
CMasternodeBroadcast::CMasternodeBroadcast()
|
|
{
|
|
vin = CTxIn();
|
|
addr = CService();
|
|
pubKeyCollateralAddress = CPubKey();
|
|
pubKeyMasternode1 = CPubKey();
|
|
sig = std::vector<unsigned char>();
|
|
activeState = MASTERNODE_ENABLED;
|
|
sigTime = GetAdjustedTime();
|
|
lastPing = CMasternodePing();
|
|
cacheInputAge = 0;
|
|
cacheInputAgeBlock = 0;
|
|
unitTest = false;
|
|
allowFreeTx = true;
|
|
protocolVersion = PROTOCOL_VERSION;
|
|
nLastDsq = 0;
|
|
nScanningErrorCount = 0;
|
|
nLastScanningErrorBlockHeight = 0;
|
|
}
|
|
|
|
CMasternodeBroadcast::CMasternodeBroadcast(CService newAddr, CTxIn newVin, CPubKey pubKeyCollateralAddressNew, CPubKey pubKeyMasternodeNew, int protocolVersionIn)
|
|
{
|
|
vin = newVin;
|
|
addr = newAddr;
|
|
pubKeyCollateralAddress = pubKeyCollateralAddressNew;
|
|
pubKeyMasternode = pubKeyMasternodeNew;
|
|
sig = std::vector<unsigned char>();
|
|
activeState = MASTERNODE_ENABLED;
|
|
sigTime = GetAdjustedTime();
|
|
lastPing = CMasternodePing();
|
|
cacheInputAge = 0;
|
|
cacheInputAgeBlock = 0;
|
|
unitTest = false;
|
|
allowFreeTx = true;
|
|
protocolVersion = protocolVersionIn;
|
|
nLastDsq = 0;
|
|
nScanningErrorCount = 0;
|
|
nLastScanningErrorBlockHeight = 0;
|
|
}
|
|
|
|
CMasternodeBroadcast::CMasternodeBroadcast(const CMasternode& mn)
|
|
{
|
|
vin = mn.vin;
|
|
addr = mn.addr;
|
|
pubKeyCollateralAddress = mn.pubKeyCollateralAddress;
|
|
pubKeyMasternode = mn.pubKeyMasternode;
|
|
sig = mn.sig;
|
|
activeState = mn.activeState;
|
|
sigTime = mn.sigTime;
|
|
lastPing = mn.lastPing;
|
|
cacheInputAge = mn.cacheInputAge;
|
|
cacheInputAgeBlock = mn.cacheInputAgeBlock;
|
|
unitTest = mn.unitTest;
|
|
allowFreeTx = mn.allowFreeTx;
|
|
protocolVersion = mn.protocolVersion;
|
|
nLastDsq = mn.nLastDsq;
|
|
nScanningErrorCount = mn.nScanningErrorCount;
|
|
nLastScanningErrorBlockHeight = mn.nLastScanningErrorBlockHeight;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Create(std::string strService, std::string strKeyMasternode, std::string strTxHash, std::string strOutputIndex, std::string& strErrorRet, CMasternodeBroadcast& mnbRet, bool fOffline)
|
|
{
|
|
CTxIn txin;
|
|
CPubKey pubKeyCollateralAddressNew;
|
|
CKey keyCollateralAddressNew;
|
|
CPubKey pubKeyMasternodeNew;
|
|
CKey keyMasternodeNew;
|
|
|
|
//need correct blocks to send ping
|
|
if (!fOffline && !masternodeSync.IsBlockchainSynced()) {
|
|
strErrorRet = "Sync in progress. Must wait until sync is complete to start Masternode";
|
|
LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
}
|
|
|
|
if (!obfuScationSigner.GetKeysFromSecret(strKeyMasternode, keyMasternodeNew, pubKeyMasternodeNew)) {
|
|
strErrorRet = strprintf("Invalid masternode key %s", strKeyMasternode);
|
|
LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
}
|
|
|
|
if (!pwalletMain->GetMasternodeVinAndKeys(txin, pubKeyCollateralAddressNew, keyCollateralAddressNew, strTxHash, strOutputIndex)) {
|
|
strErrorRet = strprintf("Could not allocate txin %s:%s for masternode %s", strTxHash, strOutputIndex, strService);
|
|
LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
return false;
|
|
}
|
|
|
|
// The service needs the correct default port to work properly
|
|
if(!CheckDefaultPort(strService, strErrorRet, "CMasternodeBroadcast::Create"))
|
|
return false;
|
|
|
|
return Create(txin, CService(strService), keyCollateralAddressNew, pubKeyCollateralAddressNew, keyMasternodeNew, pubKeyMasternodeNew, strErrorRet, mnbRet);
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Create(CTxIn txin, CService service, CKey keyCollateralAddressNew, CPubKey pubKeyCollateralAddressNew, CKey keyMasternodeNew, CPubKey pubKeyMasternodeNew, std::string& strErrorRet, CMasternodeBroadcast& mnbRet)
|
|
{
|
|
// wait for reindex and/or import to finish
|
|
if (fImporting || fReindex) return false;
|
|
|
|
LogPrint("masternode", "CMasternodeBroadcast::Create -- pubKeyCollateralAddressNew = %s, pubKeyMasternodeNew.GetID() = %s\n",
|
|
CBitcoinAddress(pubKeyCollateralAddressNew.GetID()).ToString(),
|
|
pubKeyMasternodeNew.GetID().ToString());
|
|
|
|
CMasternodePing mnp(txin);
|
|
if (!mnp.Sign(keyMasternodeNew, pubKeyMasternodeNew)) {
|
|
strErrorRet = strprintf("Failed to sign ping, masternode=%s", txin.prevout.hash.ToString());
|
|
LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
mnbRet = CMasternodeBroadcast();
|
|
return false;
|
|
}
|
|
|
|
mnbRet = CMasternodeBroadcast(service, txin, pubKeyCollateralAddressNew, pubKeyMasternodeNew, PROTOCOL_VERSION);
|
|
|
|
if (!mnbRet.IsValidNetAddr()) {
|
|
strErrorRet = strprintf("Invalid IP address %s, masternode=%s", mnbRet.addr.ToStringIP (), txin.prevout.hash.ToString());
|
|
LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
mnbRet = CMasternodeBroadcast();
|
|
return false;
|
|
}
|
|
|
|
mnbRet.lastPing = mnp;
|
|
if (!mnbRet.Sign(keyCollateralAddressNew)) {
|
|
strErrorRet = strprintf("Failed to sign broadcast, masternode=%s", txin.prevout.hash.ToString());
|
|
LogPrint("masternode","CMasternodeBroadcast::Create -- %s\n", strErrorRet);
|
|
mnbRet = CMasternodeBroadcast();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::CheckDefaultPort(std::string strService, std::string& strErrorRet, std::string strContext)
|
|
{
|
|
CService service = CService(strService);
|
|
int nDefaultPort = Params().GetDefaultPort();
|
|
|
|
if (service.GetPort() != nDefaultPort) {
|
|
strErrorRet = strprintf("Invalid port %u for masternode %s, only %d is supported on %s-net.",
|
|
service.GetPort(), strService, nDefaultPort, Params().NetworkIDString());
|
|
LogPrint("masternode", "%s - %s\n", strContext, strErrorRet);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::CheckAndUpdate(int& nDos)
|
|
{
|
|
// make sure signature isn't in the future (past is OK)
|
|
if (sigTime > GetAdjustedTime() + 60 * 60) {
|
|
LogPrint("masternode","mnb - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString());
|
|
nDos = 1;
|
|
return false;
|
|
}
|
|
|
|
// incorrect ping or its sigTime
|
|
if(lastPing == CMasternodePing() || !lastPing.CheckAndUpdate(nDos, false, true))
|
|
return false;
|
|
|
|
if (protocolVersion < masternodePayments.GetMinMasternodePaymentsProto()) {
|
|
LogPrint("masternode","mnb - ignoring outdated Masternode %s protocol version %d\n", vin.prevout.hash.ToString(), protocolVersion);
|
|
return false;
|
|
}
|
|
|
|
CScript pubkeyScript;
|
|
pubkeyScript = GetScriptForDestination(pubKeyCollateralAddress.GetID());
|
|
|
|
if (pubkeyScript.size() != 25) {
|
|
LogPrint("masternode","mnb - pubkey the wrong size\n");
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
|
|
CScript pubkeyScript2;
|
|
pubkeyScript2 = GetScriptForDestination(pubKeyMasternode.GetID());
|
|
|
|
if (pubkeyScript2.size() != 25) {
|
|
LogPrint("masternode","mnb - pubkey2 the wrong size\n");
|
|
nDos = 100;
|
|
return false;
|
|
}
|
|
|
|
if (!vin.scriptSig.empty()) {
|
|
LogPrint("masternode","mnb - Ignore Not Empty ScriptSig %s\n", vin.prevout.hash.ToString());
|
|
return false;
|
|
}
|
|
|
|
std::string errorMessage = "";
|
|
if (!obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, GetNewStrMessage(), errorMessage)
|
|
&& !obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, GetOldStrMessage(), errorMessage))
|
|
{
|
|
// don't ban for old masternodes, their sigs could be broken because of the bug
|
|
nDos = protocolVersion < MIN_PEER_MNANNOUNCE ? 0 : 100;
|
|
return error("CMasternodeBroadcast::CheckAndUpdate - Got bad Masternode address signature : %s", errorMessage);
|
|
}
|
|
|
|
if (Params().NetworkID() == CBaseChainParams::MAIN) {
|
|
if (addr.GetPort() != 51336) return false;
|
|
} else if (addr.GetPort() == 51336)
|
|
return false;
|
|
|
|
//search existing Masternode list, this is where we update existing Masternodes with new mnb broadcasts
|
|
CMasternode* pmn = mnodeman.Find(vin);
|
|
|
|
// no such masternode, nothing to update
|
|
if (pmn == NULL) return true;
|
|
|
|
// this broadcast is older or equal than the one that we already have - it's bad and should never happen
|
|
// unless someone is doing something fishy
|
|
// (mapSeenMasternodeBroadcast in CMasternodeMan::ProcessMessage should filter legit duplicates)
|
|
if(pmn->sigTime >= sigTime) {
|
|
return error("CMasternodeBroadcast::CheckAndUpdate - Bad sigTime %d for Masternode %20s %105s (existing broadcast is at %d)",
|
|
sigTime, addr.ToString(), vin.ToString(), pmn->sigTime);
|
|
}
|
|
|
|
// masternode is not enabled yet/already, nothing to update
|
|
if (!pmn->IsEnabled()) return true;
|
|
|
|
// mn.pubkey = pubkey, IsVinAssociatedWithPubkey is validated once below,
|
|
// after that they just need to match
|
|
if (pmn->pubKeyCollateralAddress == pubKeyCollateralAddress && !pmn->IsBroadcastedWithin(MASTERNODE_MIN_MNB_SECONDS)) {
|
|
//take the newest entry
|
|
LogPrint("masternode","mnb - Got updated entry for %s\n", vin.prevout.hash.ToString());
|
|
if (pmn->UpdateFromNewBroadcast((*this))) {
|
|
pmn->Check();
|
|
if (pmn->IsEnabled()) Relay();
|
|
}
|
|
masternodeSync.AddedMasternodeList(GetHash());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodeBroadcast::CheckInputsAndAdd(int& nDoS)
|
|
{
|
|
// we are a masternode with the same vin (i.e. already activated) and this mnb is ours (matches our Masternode privkey)
|
|
// so nothing to do here for us
|
|
if (fMasterNode && vin.prevout == activeMasternode.vin.prevout && pubKeyMasternode == activeMasternode.pubKeyMasternode)
|
|
return true;
|
|
|
|
// incorrect ping or its sigTime
|
|
if(lastPing == CMasternodePing() || !lastPing.CheckAndUpdate(nDoS, false, true)) return false;
|
|
|
|
// search existing Masternode list
|
|
CMasternode* pmn = mnodeman.Find(vin);
|
|
|
|
if (pmn != NULL) {
|
|
// nothing to do here if we already know about this masternode and it's enabled
|
|
if (pmn->IsEnabled()) return true;
|
|
// if it's not enabled, remove old MN first and continue
|
|
else
|
|
mnodeman.Remove(pmn->vin);
|
|
}
|
|
|
|
CValidationState state;
|
|
CMutableTransaction tx = CMutableTransaction();
|
|
CTxOut vout = CTxOut(9999.99 * COIN, obfuScationPool.collateralPubKey);
|
|
tx.vin.push_back(vin);
|
|
tx.vout.push_back(vout);
|
|
|
|
{
|
|
TRY_LOCK(cs_main, lockMain);
|
|
if (!lockMain) {
|
|
// not mnb fault, let it to be checked again later
|
|
mnodeman.mapSeenMasternodeBroadcast.erase(GetHash());
|
|
masternodeSync.mapSeenSyncMNB.erase(GetHash());
|
|
return false;
|
|
}
|
|
|
|
if (!AcceptableInputs(mempool, state, CTransaction(tx), false, NULL)) {
|
|
//set nDos
|
|
state.IsInvalid(nDoS);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LogPrint("masternode", "mnb - Accepted Masternode entry\n");
|
|
|
|
if (GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS) {
|
|
LogPrint("masternode","mnb - Input must have at least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS);
|
|
// maybe we miss few blocks, let this mnb to be checked again later
|
|
mnodeman.mapSeenMasternodeBroadcast.erase(GetHash());
|
|
masternodeSync.mapSeenSyncMNB.erase(GetHash());
|
|
return false;
|
|
}
|
|
|
|
// verify that sig time is legit in past
|
|
// should be at least not earlier than block when 1000 AGR tx got MASTERNODE_MIN_CONFIRMATIONS
|
|
uint256 hashBlock = 0;
|
|
CTransaction tx2;
|
|
GetTransaction(vin.prevout.hash, tx2, hashBlock, true);
|
|
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
|
|
if (mi != mapBlockIndex.end() && (*mi).second) {
|
|
CBlockIndex* pMNIndex = (*mi).second; // block for 1000 Agrarian tx -> 1 confirmation
|
|
CBlockIndex* pConfIndex = chainActive[pMNIndex->nHeight + MASTERNODE_MIN_CONFIRMATIONS - 1]; // block where tx got MASTERNODE_MIN_CONFIRMATIONS
|
|
if (pConfIndex->GetBlockTime() > sigTime) {
|
|
LogPrint("masternode","mnb - Bad sigTime %d for Masternode %s (%i conf block is at %d)\n",
|
|
sigTime, vin.prevout.hash.ToString(), MASTERNODE_MIN_CONFIRMATIONS, pConfIndex->GetBlockTime());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LogPrint("masternode","mnb - Got NEW Masternode entry - %s - %lli \n", vin.prevout.hash.ToString(), sigTime);
|
|
CMasternode mn(*this);
|
|
mnodeman.Add(mn);
|
|
|
|
// if it matches our Masternode privkey, then we've been remotely activated
|
|
if (pubKeyMasternode == activeMasternode.pubKeyMasternode && protocolVersion == PROTOCOL_VERSION) {
|
|
activeMasternode.EnableHotColdMasterNode(vin, addr);
|
|
}
|
|
|
|
bool isLocal = addr.IsRFC1918() || addr.IsLocal();
|
|
if (Params().NetworkID() == CBaseChainParams::REGTEST) isLocal = false;
|
|
|
|
if (!isLocal) Relay();
|
|
|
|
return true;
|
|
}
|
|
|
|
void CMasternodeBroadcast::Relay()
|
|
{
|
|
CInv inv(MSG_MASTERNODE_ANNOUNCE, GetHash());
|
|
RelayInv(inv);
|
|
}
|
|
|
|
bool CMasternodeBroadcast::Sign(CKey& keyCollateralAddress)
|
|
{
|
|
std::string errorMessage;
|
|
sigTime = GetAdjustedTime();
|
|
|
|
std::string strMessage;
|
|
if(chainActive.Height() < Params().Zerocoin_Block_V2_Start())
|
|
strMessage = GetOldStrMessage();
|
|
else
|
|
strMessage = GetNewStrMessage();
|
|
|
|
if (!obfuScationSigner.SignMessage(strMessage, errorMessage, sig, keyCollateralAddress))
|
|
return error("CMasternodeBroadcast::Sign() - Error: %s", errorMessage);
|
|
|
|
if (!obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, strMessage, errorMessage))
|
|
return error("CMasternodeBroadcast::Sign() - Error: %s", errorMessage);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CMasternodeBroadcast::VerifySignature()
|
|
{
|
|
std::string errorMessage;
|
|
|
|
if(!obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, GetNewStrMessage(), errorMessage)
|
|
&& !obfuScationSigner.VerifyMessage(pubKeyCollateralAddress, sig, GetOldStrMessage(), errorMessage))
|
|
return error("CMasternodeBroadcast::VerifySignature() - Error: %s", errorMessage);
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string CMasternodeBroadcast::GetOldStrMessage()
|
|
{
|
|
std::string strMessage;
|
|
|
|
std::string vchPubKey(pubKeyCollateralAddress.begin(), pubKeyCollateralAddress.end());
|
|
std::string vchPubKey2(pubKeyMasternode.begin(), pubKeyMasternode.end());
|
|
strMessage = addr.ToString() + std::to_string(sigTime) + vchPubKey + vchPubKey2 + std::to_string(protocolVersion);
|
|
|
|
return strMessage;
|
|
}
|
|
|
|
std:: string CMasternodeBroadcast::GetNewStrMessage()
|
|
{
|
|
std::string strMessage;
|
|
|
|
strMessage = addr.ToString() + std::to_string(sigTime) + pubKeyCollateralAddress.GetID().ToString() + pubKeyMasternode.GetID().ToString() + std::to_string(protocolVersion);
|
|
|
|
return strMessage;
|
|
}
|
|
|
|
CMasternodePing::CMasternodePing()
|
|
{
|
|
vin = CTxIn();
|
|
blockHash = uint256(0);
|
|
sigTime = 0;
|
|
vchSig = std::vector<unsigned char>();
|
|
}
|
|
|
|
CMasternodePing::CMasternodePing(CTxIn& newVin)
|
|
{
|
|
vin = newVin;
|
|
blockHash = chainActive[chainActive.Height() - 12]->GetBlockHash();
|
|
sigTime = GetAdjustedTime();
|
|
vchSig = std::vector<unsigned char>();
|
|
}
|
|
|
|
|
|
bool CMasternodePing::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
|
|
{
|
|
std::string errorMessage;
|
|
std::string strMasterNodeSignMessage;
|
|
|
|
sigTime = GetAdjustedTime();
|
|
std::string strMessage = vin.ToString() + blockHash.ToString() + std::to_string(sigTime);
|
|
|
|
if (!obfuScationSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) {
|
|
LogPrint("masternode","CMasternodePing::Sign() - Error: %s\n", errorMessage);
|
|
return false;
|
|
}
|
|
|
|
if (!obfuScationSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) {
|
|
LogPrint("masternode","CMasternodePing::Sign() - Error: %s\n", errorMessage);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePing::VerifySignature(CPubKey& pubKeyMasternode, int &nDos)
|
|
{
|
|
std::string strMessage = vin.ToString() + blockHash.ToString() + std::to_string(sigTime);
|
|
std::string errorMessage = "";
|
|
|
|
if(!obfuScationSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)){
|
|
nDos = 33;
|
|
return error("CMasternodePing::VerifySignature - Got bad Masternode ping signature %s Error: %s", vin.ToString(), errorMessage);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePing::CheckAndUpdate(int& nDos, bool fRequireEnabled, bool fCheckSigTimeOnly)
|
|
{
|
|
if (sigTime > GetAdjustedTime() + 60 * 60) {
|
|
LogPrint("masternode","CMasternodePing::CheckAndUpdate - Signature rejected, too far into the future %s\n", vin.prevout.hash.ToString());
|
|
nDos = 1;
|
|
return false;
|
|
}
|
|
|
|
if (sigTime <= GetAdjustedTime() - 60 * 60) {
|
|
LogPrint("masternode","CMasternodePing::CheckAndUpdate - Signature rejected, too far into the past %s - %d %d \n", vin.prevout.hash.ToString(), sigTime, GetAdjustedTime());
|
|
nDos = 1;
|
|
return false;
|
|
}
|
|
|
|
if(fCheckSigTimeOnly) {
|
|
CMasternode* pmn = mnodeman.Find(vin);
|
|
if(pmn) return VerifySignature(pmn->pubKeyMasternode, nDos);
|
|
return true;
|
|
}
|
|
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate - New Ping - %s - %s - %lli\n", GetHash().ToString(), blockHash.ToString(), sigTime);
|
|
|
|
// see if we have this Masternode
|
|
CMasternode* pmn = mnodeman.Find(vin);
|
|
if (pmn != NULL && pmn->protocolVersion >= masternodePayments.GetMinMasternodePaymentsProto()) {
|
|
if (fRequireEnabled && !pmn->IsEnabled()) return false;
|
|
|
|
// LogPrint("masternode","mnping - Found corresponding mn for vin: %s\n", vin.ToString());
|
|
// update only if there is no known ping for this masternode or
|
|
// last ping was more then MASTERNODE_MIN_MNP_SECONDS-60 ago comparing to this one
|
|
if (!pmn->IsPingedWithin(MASTERNODE_MIN_MNP_SECONDS - 60, sigTime)) {
|
|
if (!VerifySignature(pmn->pubKeyMasternode, nDos))
|
|
return false;
|
|
|
|
BlockMap::iterator mi = mapBlockIndex.find(blockHash);
|
|
if (mi != mapBlockIndex.end() && (*mi).second) {
|
|
if ((*mi).second->nHeight < chainActive.Height() - 24) {
|
|
LogPrint("masternode","CMasternodePing::CheckAndUpdate - Masternode %s block hash %s is too old\n", vin.prevout.hash.ToString(), blockHash.ToString());
|
|
// Do nothing here (no Masternode update, no mnping relay)
|
|
// Let this node to be visible but fail to accept mnping
|
|
|
|
return false;
|
|
}
|
|
} else {
|
|
if (fDebug) LogPrint("masternode","CMasternodePing::CheckAndUpdate - Masternode %s block hash %s is unknown\n", vin.prevout.hash.ToString(), blockHash.ToString());
|
|
// maybe we stuck so we shouldn't ban this node, just fail to accept it
|
|
// TODO: or should we also request this block?
|
|
|
|
return false;
|
|
}
|
|
|
|
pmn->lastPing = *this;
|
|
|
|
//mnodeman.mapSeenMasternodeBroadcast.lastPing is probably outdated, so we'll update it
|
|
CMasternodeBroadcast mnb(*pmn);
|
|
uint256 hash = mnb.GetHash();
|
|
if (mnodeman.mapSeenMasternodeBroadcast.count(hash)) {
|
|
mnodeman.mapSeenMasternodeBroadcast[hash].lastPing = *this;
|
|
}
|
|
|
|
pmn->Check(true);
|
|
if (!pmn->IsEnabled()) return false;
|
|
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate - Masternode ping accepted, vin: %s\n", vin.prevout.hash.ToString());
|
|
|
|
Relay();
|
|
return true;
|
|
}
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate - Masternode ping arrived too early, vin: %s\n", vin.prevout.hash.ToString());
|
|
//nDos = 1; //disable, this is happening frequently and causing banned peers
|
|
return false;
|
|
}
|
|
LogPrint("masternode", "CMasternodePing::CheckAndUpdate - Couldn't find compatible Masternode entry, vin: %s\n", vin.prevout.hash.ToString());
|
|
|
|
return false;
|
|
}
|
|
|
|
void CMasternodePing::Relay()
|
|
{
|
|
CInv inv(MSG_MASTERNODE_PING, GetHash());
|
|
RelayInv(inv);
|
|
}
|