826b18c41f
- Made masternodes/darksend compatible with regression testing mode (a local-only blockchain that doesn't require mining). Developers can now test multiple rounds in a few minutes without waiting on mining (much faster). - Added dsee security verification to v11 - darkSendMasternodes -> vecMasternodes (must clearer)
833 lines
27 KiB
C++
833 lines
27 KiB
C++
#include "masternode.h"
|
|
#include "activemasternode.h"
|
|
#include "darksend.h"
|
|
#include "core.h"
|
|
#include "util.h"
|
|
#include "addrman.h"
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
/** The list of active masternodes */
|
|
std::vector<CMasterNode> vecMasternodes;
|
|
/** Object for who's going to get paid on which blocks */
|
|
CMasternodePayments masternodePayments;
|
|
// keep track of masternode votes I've seen
|
|
map<uint256, int> mapSeenMasternodeVotes;
|
|
// keep track of the scanning errors I've seen
|
|
map<uint256, int> mapSeenMasternodeScanningErrors;
|
|
// who's asked for the masternode list and the last time
|
|
std::map<CNetAddr, int64_t> askedForMasternodeList;
|
|
// which masternodes we've asked for
|
|
std::map<COutPoint, int64_t> askedForMasternodeListEntry;
|
|
|
|
// manage the masternode connections
|
|
void ProcessMasternodeConnections(){
|
|
LOCK(cs_vNodes);
|
|
|
|
BOOST_FOREACH(CNode* pnode, vNodes)
|
|
{
|
|
//if it's our masternode, let it be
|
|
if((CNetAddr)darkSendPool.submittedToMasternode == (CNetAddr)pnode->addr) continue;
|
|
|
|
if(pnode->fDarkSendMaster){
|
|
LogPrintf("Closing masternode connection %s \n", pnode->addr.ToString().c_str());
|
|
pnode->CloseSocketDisconnect();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcessMessageMasternode(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
|
|
{
|
|
if (strCommand == "dsee") { //DarkSend Election Entry
|
|
bool fIsInitialDownload = IsInitialBlockDownload();
|
|
if(fIsInitialDownload) return;
|
|
|
|
CTxIn vin;
|
|
CService addr;
|
|
CPubKey pubkey;
|
|
CPubKey pubkey2;
|
|
vector<unsigned char> vchSig;
|
|
int64_t sigTime;
|
|
int count;
|
|
int current;
|
|
int64_t lastUpdated;
|
|
int protocolVersion;
|
|
std::string strMessage;
|
|
|
|
// 70047 and greater
|
|
vRecv >> vin >> addr >> vchSig >> sigTime >> pubkey >> pubkey2 >> count >> current >> lastUpdated >> protocolVersion;
|
|
|
|
// make sure signature isn't in the future (past is OK)
|
|
if (sigTime > GetAdjustedTime() + 60 * 60) {
|
|
LogPrintf("dsee - Signature rejected, too far into the future %s\n", vin.ToString().c_str());
|
|
return;
|
|
}
|
|
|
|
bool isLocal = addr.IsRFC1918();
|
|
std::string vchPubKey(pubkey.begin(), pubkey.end());
|
|
std::string vchPubKey2(pubkey2.begin(), pubkey2.end());
|
|
|
|
strMessage = addr.ToString() + boost::lexical_cast<std::string>(sigTime) + vchPubKey + vchPubKey2 + boost::lexical_cast<std::string>(protocolVersion);
|
|
|
|
if(protocolVersion < nMasternodeMinProtocol) {
|
|
LogPrintf("dsee - ignoring outdated masternode %s protocol version %d\n", vin.ToString().c_str(), protocolVersion);
|
|
return;
|
|
}
|
|
|
|
CScript pubkeyScript;
|
|
pubkeyScript.SetDestination(pubkey.GetID());
|
|
|
|
if(pubkeyScript.size() != 25) {
|
|
LogPrintf("dsee - pubkey the wrong size\n");
|
|
Misbehaving(pfrom->GetId(), 100);
|
|
return;
|
|
}
|
|
|
|
CScript pubkeyScript2;
|
|
pubkeyScript2.SetDestination(pubkey2.GetID());
|
|
|
|
if(pubkeyScript2.size() != 25) {
|
|
LogPrintf("dsee - pubkey2 the wrong size\n");
|
|
Misbehaving(pfrom->GetId(), 100);
|
|
return;
|
|
}
|
|
|
|
std::string errorMessage = "";
|
|
if(!darkSendSigner.VerifyMessage(pubkey, vchSig, strMessage, errorMessage)){
|
|
LogPrintf("dsee - Got bad masternode address signature\n");
|
|
Misbehaving(pfrom->GetId(), 100);
|
|
return;
|
|
}
|
|
|
|
if(
|
|
(Params().NetworkID() == CChainParams::TESTNET && addr.GetPort() != 19999) ||
|
|
(Params().NetworkID() == CChainParams::REGTEST && addr.GetPort() != 19999) ||
|
|
(Params().NetworkID() == CChainParams::MAIN && addr.GetPort() != 9999)) return;
|
|
|
|
//search existing masternode list, this is where we update existing masternodes with new dsee broadcasts
|
|
|
|
BOOST_FOREACH(CMasterNode& mn, vecMasternodes) {
|
|
if(mn.vin.prevout == vin.prevout) {
|
|
// count == -1 when it's a new entry
|
|
// e.g. We don't want the entry relayed/time updated when we're syncing the list
|
|
// mn.pubkey = pubkey, IsVinAssociatedWithPubkey is validated once below,
|
|
// after that they just need to match
|
|
if(count == -1 && mn.pubkey == pubkey && !mn.UpdatedWithin(MASTERNODE_MIN_DSEE_SECONDS)){
|
|
mn.UpdateLastSeen();
|
|
|
|
if(mn.now < sigTime){ //take the newest entry
|
|
LogPrintf("dsee - Got updated entry for %s\n", addr.ToString().c_str());
|
|
mn.pubkey2 = pubkey2;
|
|
mn.now = sigTime;
|
|
mn.sig = vchSig;
|
|
mn.protocolVersion = protocolVersion;
|
|
mn.addr = addr;
|
|
|
|
RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// make sure the vout that was signed is related to the transaction that spawned the masternode
|
|
// - this is expensive, so it's only done once per masternode
|
|
if(!darkSendSigner.IsVinAssociatedWithPubkey(vin, pubkey)) {
|
|
LogPrintf("dsee - Got mismatched pubkey and vin\n");
|
|
Misbehaving(pfrom->GetId(), 100);
|
|
return;
|
|
}
|
|
|
|
if(fDebug) LogPrintf("dsee - Got NEW masternode entry %s\n", addr.ToString().c_str());
|
|
|
|
// make sure it's still unspent
|
|
// - this is checked later by .check() in many places and by ThreadCheckDarkSendPool()
|
|
|
|
CValidationState state;
|
|
CTransaction tx = CTransaction();
|
|
CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey);
|
|
tx.vin.push_back(vin);
|
|
tx.vout.push_back(vout);
|
|
if(AcceptableInputs(mempool, state, tx)){
|
|
if(fDebug) LogPrintf("dsee - Accepted masternode entry %i %i\n", count, current);
|
|
|
|
if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){
|
|
LogPrintf("dsee - Input must have least %d confirmations\n", MASTERNODE_MIN_CONFIRMATIONS);
|
|
Misbehaving(pfrom->GetId(), 20);
|
|
return;
|
|
}
|
|
|
|
// use this as a peer
|
|
addrman.Add(CAddress(addr), pfrom->addr, 2*60*60);
|
|
|
|
// add our masternode
|
|
CMasterNode mn(addr, vin, pubkey, vchSig, sigTime, pubkey2, protocolVersion);
|
|
mn.UpdateLastSeen(lastUpdated);
|
|
vecMasternodes.push_back(mn);
|
|
|
|
// if it matches our masternodeprivkey, then we've been remotely activated
|
|
if(pubkey2 == activeMasternode.pubKeyMasternode && protocolVersion == PROTOCOL_VERSION){
|
|
activeMasternode.EnableHotColdMasterNode(vin, addr);
|
|
}
|
|
|
|
if(count == -1 && !isLocal)
|
|
RelayDarkSendElectionEntry(vin, addr, vchSig, sigTime, pubkey, pubkey2, count, current, lastUpdated, protocolVersion);
|
|
|
|
} else {
|
|
LogPrintf("dsee - Rejected masternode entry %s\n", addr.ToString().c_str());
|
|
|
|
int nDoS = 0;
|
|
if (state.IsInvalid(nDoS))
|
|
{
|
|
LogPrintf("dsee - %s from %s %s was not accepted into the memory pool\n", tx.GetHash().ToString().c_str(),
|
|
pfrom->addr.ToString().c_str(), pfrom->cleanSubVer.c_str());
|
|
if (nDoS > 0)
|
|
Misbehaving(pfrom->GetId(), nDoS);
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (strCommand == "dseep") { //DarkSend Election Entry Ping
|
|
bool fIsInitialDownload = IsInitialBlockDownload();
|
|
if(fIsInitialDownload) return;
|
|
|
|
CTxIn vin;
|
|
vector<unsigned char> vchSig;
|
|
int64_t sigTime;
|
|
bool stop;
|
|
vRecv >> vin >> vchSig >> sigTime >> stop;
|
|
|
|
//LogPrintf("dseep - Received: vin: %s sigTime: %lld stop: %s\n", vin.ToString().c_str(), sigTime, stop ? "true" : "false");
|
|
|
|
if (sigTime > GetAdjustedTime() + 60 * 60) {
|
|
LogPrintf("dseep - Signature rejected, too far into the future %s\n", vin.ToString().c_str());
|
|
return;
|
|
}
|
|
|
|
if (sigTime <= GetAdjustedTime() - 60 * 60) {
|
|
LogPrintf("dseep - Signature rejected, too far into the past %s - %d %d \n", vin.ToString().c_str(), sigTime, GetAdjustedTime());
|
|
return;
|
|
}
|
|
|
|
// see if we have this masternode
|
|
|
|
BOOST_FOREACH(CMasterNode& mn, vecMasternodes) {
|
|
if(mn.vin.prevout == vin.prevout) {
|
|
// LogPrintf("dseep - Found corresponding mn for vin: %s\n", vin.ToString().c_str());
|
|
// take this only if it's newer
|
|
if(mn.lastDseep < sigTime){
|
|
std::string strMessage = mn.addr.ToString() + boost::lexical_cast<std::string>(sigTime) + boost::lexical_cast<std::string>(stop);
|
|
|
|
std::string errorMessage = "";
|
|
if(!darkSendSigner.VerifyMessage(mn.pubkey2, vchSig, strMessage, errorMessage)){
|
|
LogPrintf("dseep - Got bad masternode address signature %s \n", vin.ToString().c_str());
|
|
//Misbehaving(pfrom->GetId(), 100);
|
|
return;
|
|
}
|
|
|
|
mn.lastDseep = sigTime;
|
|
|
|
if(!mn.UpdatedWithin(MASTERNODE_MIN_DSEEP_SECONDS)){
|
|
mn.UpdateLastSeen();
|
|
if(stop) {
|
|
mn.Disable();
|
|
mn.Check();
|
|
}
|
|
RelayDarkSendElectionEntryPing(vin, vchSig, sigTime, stop);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(fDebug) LogPrintf("dseep - Couldn't find masternode entry %s\n", vin.ToString().c_str());
|
|
|
|
std::map<COutPoint, int64_t>::iterator i = askedForMasternodeListEntry.find(vin.prevout);
|
|
if (i != askedForMasternodeListEntry.end()){
|
|
int64_t t = (*i).second;
|
|
if (GetTime() < t) {
|
|
// we've asked recently
|
|
return;
|
|
}
|
|
}
|
|
|
|
// ask for the dsee info once from the node that sent dseep
|
|
|
|
LogPrintf("dseep - Asking source node for missing entry %s\n", vin.ToString().c_str());
|
|
pfrom->PushMessage("dseg", vin);
|
|
int64_t askAgain = GetTime()+(60*60*24);
|
|
askedForMasternodeListEntry[vin.prevout] = askAgain;
|
|
|
|
} else if (strCommand == "dseg") { //Get masternode list or specific entry
|
|
CTxIn vin;
|
|
vRecv >> vin;
|
|
|
|
if(vin == CTxIn()) { //only should ask for this once
|
|
//local network
|
|
if(!pfrom->addr.IsRFC1918())
|
|
{
|
|
std::map<CNetAddr, int64_t>::iterator i = askedForMasternodeList.find(pfrom->addr);
|
|
if (i != askedForMasternodeList.end())
|
|
{
|
|
int64_t t = (*i).second;
|
|
if (GetTime() < t) {
|
|
Misbehaving(pfrom->GetId(), 100);
|
|
LogPrintf("dseg - peer already asked me for the list\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
int64_t askAgain = GetTime()+(60*60*24);
|
|
askedForMasternodeList[pfrom->addr] = askAgain;
|
|
}
|
|
} //else, asking for a specific node which is ok
|
|
|
|
int count = vecMasternodes.size()-1;
|
|
int i = 0;
|
|
|
|
BOOST_FOREACH(CMasterNode mn, vecMasternodes) {
|
|
|
|
if(mn.addr.IsRFC1918()) continue; //local network
|
|
|
|
if(vin == CTxIn()){
|
|
mn.Check();
|
|
if(mn.IsEnabled()) {
|
|
if(fDebug) LogPrintf("dseg - Sending masternode entry - %s \n", mn.addr.ToString().c_str());
|
|
pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.now, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion);
|
|
}
|
|
} else if (vin == mn.vin) {
|
|
if(fDebug) LogPrintf("dseg - Sending masternode entry - %s \n", mn.addr.ToString().c_str());
|
|
pfrom->PushMessage("dsee", mn.vin, mn.addr, mn.sig, mn.now, mn.pubkey, mn.pubkey2, count, i, mn.lastTimeSeen, mn.protocolVersion);
|
|
LogPrintf("dseg - Sent 1 masternode entries to %s\n", pfrom->addr.ToString().c_str());
|
|
return;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
LogPrintf("dseg - Sent %d masternode entries to %s\n", count, pfrom->addr.ToString().c_str());
|
|
}
|
|
|
|
else if (strCommand == "mnget") { //Masternode Payments Request Sync
|
|
|
|
if(pfrom->HasFulfilledRequest("mnget")) {
|
|
LogPrintf("mnget - peer already asked me for the list\n");
|
|
Misbehaving(pfrom->GetId(), 20);
|
|
return;
|
|
}
|
|
|
|
pfrom->FulfilledRequest("mnget");
|
|
masternodePayments.Sync(pfrom);
|
|
LogPrintf("mnget - Sent masternode winners to %s\n", pfrom->addr.ToString().c_str());
|
|
}
|
|
else if (strCommand == "mnw") { //Masternode Payments Declare Winner
|
|
CMasternodePaymentWinner winner;
|
|
vRecv >> winner;
|
|
|
|
if(chainActive.Tip() == NULL) return;
|
|
|
|
uint256 hash = winner.GetHash();
|
|
if(mapSeenMasternodeVotes.count(hash)) {
|
|
if(fDebug) LogPrintf("mnw - seen vote %s Height %d bestHeight %d\n", hash.ToString().c_str(), winner.nBlockHeight, chainActive.Tip()->nHeight);
|
|
return;
|
|
}
|
|
|
|
if(winner.nBlockHeight < chainActive.Tip()->nHeight - 10 || winner.nBlockHeight > chainActive.Tip()->nHeight+20){
|
|
LogPrintf("mnw - winner out of range %s Height %d bestHeight %d\n", winner.vin.ToString().c_str(), winner.nBlockHeight, chainActive.Tip()->nHeight);
|
|
return;
|
|
}
|
|
|
|
if(winner.vin.nSequence != std::numeric_limits<unsigned int>::max()){
|
|
LogPrintf("mnw - invalid nSequence\n");
|
|
Misbehaving(pfrom->GetId(), 100);
|
|
return;
|
|
}
|
|
|
|
LogPrintf("mnw - winning vote %s Height %d bestHeight %d\n", winner.vin.ToString().c_str(), winner.nBlockHeight, chainActive.Tip()->nHeight);
|
|
|
|
if(!masternodePayments.CheckSignature(winner)){
|
|
LogPrintf("mnw - invalid signature\n");
|
|
Misbehaving(pfrom->GetId(), 100);
|
|
return;
|
|
}
|
|
|
|
mapSeenMasternodeVotes.insert(make_pair(hash, 1));
|
|
|
|
if(masternodePayments.AddWinningMasternode(winner)){
|
|
masternodePayments.Relay(winner);
|
|
}
|
|
} /*else if (strCommand == "mnse") { //Masternode Scanning Error
|
|
CMasternodeScanningError entry;
|
|
vRecv >> entry;
|
|
|
|
if(chainActive.Tip() == NULL) return;
|
|
|
|
uint256 hash = entry.GetHash();
|
|
if(mapSeenMasternodeScanningErrors.count(hash)) {
|
|
if(fDebug) LogPrintf("mnse - seen entry addr %d error %d\n", entry.addr.ToString().c_str(), entry.error.c_str());
|
|
return;
|
|
}
|
|
|
|
LogPrintf("mnse - seen entry addr %d error %d\n", entry.addr.ToString().c_str(), entry.error.c_str());
|
|
|
|
if(!masternodeScanningError.CheckSignature(entry)){
|
|
LogPrintf("mnse - invalid signature\n");
|
|
Misbehaving(pfrom->GetId(), 100);
|
|
return;
|
|
}
|
|
|
|
mapSeenMasternodeVotes.insert(make_pair(hash, 1));
|
|
|
|
if(masternodeScanningError.AddWinningMasternode(entry)){
|
|
masternodeScanningError.Relay(entry);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
struct CompareValueOnly
|
|
{
|
|
bool operator()(const pair<int64_t, CTxIn>& t1,
|
|
const pair<int64_t, CTxIn>& t2) const
|
|
{
|
|
return t1.first < t2.first;
|
|
}
|
|
};
|
|
|
|
struct CompareValueOnly2
|
|
{
|
|
bool operator()(const pair<int64_t, int>& t1,
|
|
const pair<int64_t, int>& t2) const
|
|
{
|
|
return t1.first < t2.first;
|
|
}
|
|
};
|
|
|
|
int CountMasternodesAboveProtocol(int protocolVersion)
|
|
{
|
|
int i = 0;
|
|
|
|
BOOST_FOREACH(CMasterNode& mn, vecMasternodes) {
|
|
if(mn.protocolVersion < protocolVersion) continue;
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
int GetMasternodeByVin(CTxIn& vin)
|
|
{
|
|
int i = 0;
|
|
|
|
BOOST_FOREACH(CMasterNode& mn, vecMasternodes) {
|
|
if (mn.vin == vin) return i;
|
|
i++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int GetCurrentMasterNode(int mod, int64_t nBlockHeight, int minProtocol)
|
|
{
|
|
int i = 0;
|
|
unsigned int score = 0;
|
|
int winner = -1;
|
|
|
|
// scan for winner
|
|
BOOST_FOREACH(CMasterNode mn, vecMasternodes) {
|
|
mn.Check();
|
|
if(mn.protocolVersion < minProtocol) continue;
|
|
if(!mn.IsEnabled()) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
// calculate the score for each masternode
|
|
uint256 n = mn.CalculateScore(mod, nBlockHeight);
|
|
unsigned int n2 = 0;
|
|
memcpy(&n2, &n, sizeof(n2));
|
|
|
|
// determine the winner
|
|
if(n2 > score){
|
|
score = n2;
|
|
winner = i;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
return winner;
|
|
}
|
|
|
|
int GetMasternodeByRank(int findRank, int64_t nBlockHeight, int minProtocol)
|
|
{
|
|
int i = 0;
|
|
|
|
std::vector<pair<unsigned int, int> > vecMasternodeScores;
|
|
|
|
i = 0;
|
|
BOOST_FOREACH(CMasterNode mn, vecMasternodes) {
|
|
mn.Check();
|
|
if(mn.protocolVersion < minProtocol) continue;
|
|
if(!mn.IsEnabled()) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
uint256 n = mn.CalculateScore(1, nBlockHeight);
|
|
unsigned int n2 = 0;
|
|
memcpy(&n2, &n, sizeof(n2));
|
|
|
|
vecMasternodeScores.push_back(make_pair(n2, i));
|
|
i++;
|
|
}
|
|
|
|
sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly2());
|
|
|
|
int rank = 0;
|
|
BOOST_FOREACH (PAIRTYPE(unsigned int, int)& s, vecMasternodeScores){
|
|
rank++;
|
|
if(rank == findRank) return s.second;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int GetMasternodeRank(CTxIn& vin, int64_t nBlockHeight, int minProtocol)
|
|
{
|
|
std::vector<pair<unsigned int, CTxIn> > vecMasternodeScores;
|
|
|
|
BOOST_FOREACH(CMasterNode mn, vecMasternodes) {
|
|
mn.Check();
|
|
if(mn.protocolVersion < minProtocol) continue;
|
|
if(!mn.IsEnabled()) {
|
|
continue;
|
|
}
|
|
|
|
uint256 n = mn.CalculateScore(1, nBlockHeight);
|
|
unsigned int n2 = 0;
|
|
memcpy(&n2, &n, sizeof(n2));
|
|
|
|
vecMasternodeScores.push_back(make_pair(n2, mn.vin));
|
|
}
|
|
|
|
sort(vecMasternodeScores.rbegin(), vecMasternodeScores.rend(), CompareValueOnly());
|
|
|
|
unsigned int rank = 0;
|
|
BOOST_FOREACH (PAIRTYPE(unsigned int, CTxIn)& s, vecMasternodeScores){
|
|
rank++;
|
|
if(s.second == vin) return rank;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
if(!darkSendPool.GetLastValidBlockHash(hash, mod, nBlockHeight)) return 0;
|
|
uint256 hash2 = HashX11(BEGIN(hash), END(hash));
|
|
|
|
// we'll make a 4 dimensional point in space
|
|
// the closest masternode to that point wins
|
|
uint64_t a1 = hash2.Get64(0);
|
|
uint64_t a2 = hash2.Get64(1);
|
|
uint64_t a3 = hash2.Get64(2);
|
|
uint64_t a4 = hash2.Get64(3);
|
|
|
|
//copy part of our source hash
|
|
int i1, i2, i3, i4;
|
|
i1=0;i2=0;i3=0;i4=0;
|
|
memcpy(&i1, &a1, 1);
|
|
memcpy(&i2, &a2, 1);
|
|
memcpy(&i3, &a3, 1);
|
|
memcpy(&i4, &a4, 1);
|
|
|
|
//split up our mn hash
|
|
uint64_t b1 = vin.prevout.hash.Get64(0);
|
|
uint64_t b2 = vin.prevout.hash.Get64(1);
|
|
uint64_t b3 = vin.prevout.hash.Get64(2);
|
|
uint64_t b4 = vin.prevout.hash.Get64(3);
|
|
|
|
//move mn hash around
|
|
b1 <<= (i1 % 64);
|
|
b2 <<= (i2 % 64);
|
|
b3 <<= (i3 % 64);
|
|
b4 <<= (i4 % 64);
|
|
|
|
// calculate distance between target point and mn point
|
|
uint256 r = 0;
|
|
r += (a1 > b1 ? a1 - b1 : b1 - a1);
|
|
r += (a2 > b2 ? a2 - b2 : b2 - a2);
|
|
r += (a3 > b3 ? a3 - b3 : b3 - a3);
|
|
r += (a4 > b4 ? a4 - b4 : b4 - a4);
|
|
|
|
/*
|
|
LogPrintf(" -- MasterNode CalculateScore() n2 = %s \n", n2.ToString().c_str());
|
|
LogPrintf(" -- MasterNode CalculateScore() vin = %s \n", vin.prevout.hash.GetHex().c_str());
|
|
LogPrintf(" -- MasterNode CalculateScore() n3 = %s \n", n3.ToString().c_str());*/
|
|
|
|
return r;
|
|
}
|
|
|
|
void CMasterNode::Check()
|
|
{
|
|
//once spent, stop doing the checks
|
|
if(enabled==3) return;
|
|
|
|
|
|
if(!UpdatedWithin(MASTERNODE_REMOVAL_SECONDS)){
|
|
enabled = 4;
|
|
return;
|
|
}
|
|
|
|
if(!UpdatedWithin(MASTERNODE_EXPIRATION_SECONDS)){
|
|
enabled = 2;
|
|
return;
|
|
}
|
|
|
|
if(!unitTest){
|
|
CValidationState state;
|
|
CTransaction tx = CTransaction();
|
|
CTxOut vout = CTxOut(999.99*COIN, darkSendPool.collateralPubKey);
|
|
tx.vin.push_back(vin);
|
|
tx.vout.push_back(vout);
|
|
|
|
if(!AcceptableInputs(mempool, state, tx)){
|
|
enabled = 3;
|
|
return;
|
|
}
|
|
}
|
|
|
|
enabled = 1; // OK
|
|
}
|
|
|
|
bool CMasternodePayments::CheckSignature(CMasternodePaymentWinner& winner)
|
|
{
|
|
//note: need to investigate why this is failing
|
|
std::string strMessage = winner.vin.ToString().c_str() + boost::lexical_cast<std::string>(winner.nBlockHeight);
|
|
std::string strPubKey = (Params().NetworkID() == CChainParams::MAIN) ? strMainPubKey : strTestPubKey;
|
|
CPubKey pubkey(ParseHex(strPubKey));
|
|
|
|
std::string errorMessage = "";
|
|
if(!darkSendSigner.VerifyMessage(pubkey, winner.vchSig, strMessage, errorMessage)){
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CMasternodePayments::Sign(CMasternodePaymentWinner& winner)
|
|
{
|
|
std::string strMessage = winner.vin.ToString().c_str() + boost::lexical_cast<std::string>(winner.nBlockHeight);
|
|
|
|
CKey key2;
|
|
CPubKey pubkey2;
|
|
std::string errorMessage = "";
|
|
|
|
if(!darkSendSigner.SetKey(strMasterPrivKey, errorMessage, key2, pubkey2))
|
|
{
|
|
LogPrintf("CMasternodePayments::Sign - Invalid masternodeprivkey: '%s'\n", errorMessage.c_str());
|
|
exit(0);
|
|
}
|
|
|
|
if(!darkSendSigner.SignMessage(strMessage, errorMessage, winner.vchSig, key2)) {
|
|
LogPrintf("CMasternodePayments::Sign - Sign message failed");
|
|
return false;
|
|
}
|
|
|
|
if(!darkSendSigner.VerifyMessage(pubkey2, winner.vchSig, strMessage, errorMessage)) {
|
|
LogPrintf("CMasternodePayments::Sign - Verify message failed");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint64_t CMasternodePayments::CalculateScore(uint256 blockHash, CTxIn& vin)
|
|
{
|
|
uint256 n1 = blockHash;
|
|
uint256 n2 = HashX11(BEGIN(n1), END(n1));
|
|
uint256 n3 = HashX11(BEGIN(vin.prevout.hash), END(vin.prevout.hash));
|
|
uint256 n4 = n3 > n2 ? (n3 - n2) : (n2 - n3);
|
|
|
|
//printf(" -- CMasternodePayments CalculateScore() n2 = %d \n", n2.Get64());
|
|
//printf(" -- CMasternodePayments CalculateScore() n3 = %d \n", n3.Get64());
|
|
//printf(" -- CMasternodePayments CalculateScore() n4 = %d \n", n4.Get64());
|
|
|
|
return n4.Get64();
|
|
}
|
|
|
|
bool CMasternodePayments::GetBlockPayee(int nBlockHeight, CScript& payee)
|
|
{
|
|
BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning){
|
|
if(winner.nBlockHeight == nBlockHeight) {
|
|
|
|
CTransaction tx;
|
|
uint256 hash;
|
|
if(GetTransaction(winner.vin.prevout.hash, tx, hash, true)){
|
|
BOOST_FOREACH(CTxOut out, tx.vout){
|
|
if(out.nValue == 1000*COIN){
|
|
payee = out.scriptPubKey;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CMasternodePayments::GetWinningMasternode(int nBlockHeight, CTxIn& vinOut)
|
|
{
|
|
BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning){
|
|
if(winner.nBlockHeight == nBlockHeight) {
|
|
vinOut = winner.vin;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CMasternodePayments::AddWinningMasternode(CMasternodePaymentWinner& winnerIn)
|
|
{
|
|
uint256 blockHash = 0;
|
|
if(!darkSendPool.GetBlockHash(blockHash, winnerIn.nBlockHeight-576)) {
|
|
return false;
|
|
}
|
|
|
|
winnerIn.score = CalculateScore(blockHash, winnerIn.vin);
|
|
|
|
bool foundBlock = false;
|
|
BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning){
|
|
if(winner.nBlockHeight == winnerIn.nBlockHeight) {
|
|
foundBlock = true;
|
|
if(winner.score < winnerIn.score){
|
|
winner.score = winnerIn.score;
|
|
winner.vin = winnerIn.vin;
|
|
winner.vchSig = winnerIn.vchSig;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if it's not in the vector
|
|
if(!foundBlock){
|
|
vWinning.push_back(winnerIn);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CMasternodePayments::CleanPaymentList()
|
|
{
|
|
if(chainActive.Tip() == NULL) return;
|
|
|
|
vector<CMasternodePaymentWinner>::iterator it;
|
|
for(it=vWinning.begin();it<vWinning.end();it++){
|
|
if(chainActive.Tip()->nHeight - (*it).nBlockHeight > 1000){
|
|
if(fDebug) LogPrintf("CMasternodePayments::CleanPaymentList - Removing old masternode payment - block %d\n", (*it).nBlockHeight);
|
|
vWinning.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int CMasternodePayments::LastPayment(CMasterNode& mn)
|
|
{
|
|
if(chainActive.Tip() == NULL) return 0;
|
|
|
|
int ret = mn.GetMasternodeInputAge();
|
|
|
|
BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning){
|
|
if(winner.vin == mn.vin && chainActive.Tip()->nHeight - winner.nBlockHeight < ret)
|
|
ret = chainActive.Tip()->nHeight - winner.nBlockHeight;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool CMasternodePayments::ProcessBlock(int nBlockHeight)
|
|
{
|
|
if(strMasterPrivKey.empty()) return false;
|
|
CMasternodePaymentWinner winner;
|
|
|
|
uint256 blockHash = 0;
|
|
if(!darkSendPool.GetBlockHash(blockHash, nBlockHeight-576)) return false;
|
|
|
|
BOOST_FOREACH(CMasterNode& mn, vecMasternodes) {
|
|
mn.Check();
|
|
|
|
if(!mn.IsEnabled()) {
|
|
continue;
|
|
}
|
|
|
|
if(LastPayment(mn) < vecMasternodes.size()*.9) continue;
|
|
|
|
uint64_t score = CalculateScore(blockHash, mn.vin);
|
|
if(score > winner.score){
|
|
winner.score = score;
|
|
winner.nBlockHeight = nBlockHeight;
|
|
winner.vin = mn.vin;
|
|
}
|
|
}
|
|
|
|
if(winner.nBlockHeight == 0) return false; //no masternodes available
|
|
|
|
if(Sign(winner)){
|
|
if(AddWinningMasternode(winner)){
|
|
Relay(winner);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CMasternodePayments::Relay(CMasternodePaymentWinner& winner)
|
|
{
|
|
LOCK(cs_vNodes);
|
|
BOOST_FOREACH(CNode* pnode, vNodes){
|
|
if(!pnode->fRelayTxes)
|
|
continue;
|
|
|
|
pnode->PushMessage("mnw", winner);
|
|
}
|
|
}
|
|
|
|
void CMasternodePayments::Sync(CNode* node)
|
|
{
|
|
BOOST_FOREACH(CMasternodePaymentWinner& winner, vWinning)
|
|
if(winner.nBlockHeight >= chainActive.Tip()->nHeight-10 && winner.nBlockHeight <= chainActive.Tip()->nHeight + 20)
|
|
node->PushMessage("mnw", winner);
|
|
}
|
|
|
|
|
|
bool CMasternodePayments::SetPrivKey(std::string strPrivKey)
|
|
{
|
|
CMasternodePaymentWinner winner;
|
|
|
|
// Test signing successful, proceed
|
|
strMasterPrivKey = strPrivKey;
|
|
|
|
Sign(winner);
|
|
|
|
if(CheckSignature(winner)){
|
|
LogPrintf("CMasternodePayments::SetPrivKey - Successfully initialized as masternode payments master\n");
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|