#include "protocol.h" #include "activemasternode.h" #include "masternodeman.h" #include "masternode.h" #include "masternodeconfig.h" #include "spork.h" // // Bootup the Masternode, look for a 1000DRK input and register on the network // void CActiveMasternode::ManageStatus() { std::string errorMessage; if(!fMasterNode) return; if (fDebug) LogPrintf("CActiveMasternode::ManageStatus() - Begin\n"); //need correct blocks to send ping if(Params().NetworkID() != CBaseChainParams::REGTEST && !masternodeSync.IsBlockchainSynced()) { status = ACTIVE_MASTERNODE_SYNC_IN_PROCESS; LogPrintf("CActiveMasternode::ManageStatus() - %s\n", GetStatus()); return; } if(status == ACTIVE_MASTERNODE_SYNC_IN_PROCESS) status = ACTIVE_MASTERNODE_INITIAL; if(status == ACTIVE_MASTERNODE_INITIAL) { CMasternode *pmn; pmn = mnodeman.Find(pubKeyMasternode); if(pmn != NULL) { pmn->Check(); if(pmn->IsEnabled() && pmn->protocolVersion == PROTOCOL_VERSION) EnableHotColdMasterNode(pmn->vin, pmn->addr); } } if(status != ACTIVE_MASTERNODE_STARTED) { // Set defaults status = ACTIVE_MASTERNODE_NOT_CAPABLE; notCapableReason = ""; if(strMasterNodeAddr.empty()) { if(!GetLocal(service)) { notCapableReason = "Can't detect external address. Please use the masternodeaddr configuration option."; LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason); return; } } else { service = CService(strMasterNodeAddr); } LogPrintf("CActiveMasternode::ManageStatus() - Checking inbound connection to '%s'\n", service.ToString()); if(Params().NetworkID() == CBaseChainParams::MAIN) { if(service.GetPort() != 9999) { notCapableReason = strprintf("Invalid port: %u - only 9999 is supported on mainnet.", service.GetPort()); LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason); return; } } else if(service.GetPort() == 9999) { notCapableReason = strprintf("Invalid port: %u - 9999 is only supported on mainnet.", service.GetPort()); LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason); return; } if(!ConnectNode((CAddress)service, service.ToString().c_str())){ notCapableReason = "Could not connect to " + service.ToString(); LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason); return; } if(pwalletMain->IsLocked()){ notCapableReason = "Wallet is locked."; LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason); return; } // Choose coins to use CPubKey pubKeyCollateralAddress; CKey keyCollateralAddress; if(GetMasterNodeVin(vin, pubKeyCollateralAddress, keyCollateralAddress)) { if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){ status = ACTIVE_MASTERNODE_INPUT_TOO_NEW; notCapableReason = strprintf("%s - %d confirmations", GetStatus(), GetInputAge(vin)); LogPrintf("CActiveMasternode::ManageStatus() - %s\n", notCapableReason); return; } LOCK(pwalletMain->cs_wallet); pwalletMain->LockCoin(vin.prevout); // send to all nodes CPubKey pubKeyMasternode; CKey keyMasternode; if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode)) { notCapableReason = "Error upon calling SetKey: " + errorMessage; LogPrintf("Register::ManageStatus() - %s\n", notCapableReason); return; } if(!Register(vin, service, keyCollateralAddress, pubKeyCollateralAddress, keyMasternode, pubKeyMasternode, errorMessage)) { notCapableReason = "Error on Register: " + errorMessage; LogPrintf("Register::ManageStatus() - %s\n", notCapableReason); return; } LogPrintf("CActiveMasternode::ManageStatus() - Is capable master node!\n"); status = ACTIVE_MASTERNODE_STARTED; return; } else { notCapableReason = "Could not find suitable coins!"; LogPrintf("CActiveMasternode::ManageStatus() - %s\n", notCapableReason); return; } } //send to all peers if(!SendMasternodePing(errorMessage)) { LogPrintf("CActiveMasternode::ManageStatus() - Error on Ping: %s\n", errorMessage); } } std::string CActiveMasternode::GetStatus() { switch (status) { case ACTIVE_MASTERNODE_INITIAL: return "Node just started, not yet activated"; case ACTIVE_MASTERNODE_SYNC_IN_PROCESS: return "Sync in progress. Must wait until sync is complete to start Masternode"; case ACTIVE_MASTERNODE_INPUT_TOO_NEW: return strprintf("Masternode input must have at least %d confirmations", MASTERNODE_MIN_CONFIRMATIONS); case ACTIVE_MASTERNODE_NOT_CAPABLE: return "Not capable masternode: " + notCapableReason; case ACTIVE_MASTERNODE_STARTED: return "Masternode successfully started"; default: return "unknown"; } } bool CActiveMasternode::SendMasternodePing(std::string& errorMessage) { if(status != ACTIVE_MASTERNODE_STARTED) { errorMessage = "Masternode is not in a running status"; return false; } CPubKey pubKeyMasternode; CKey keyMasternode; if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode)) { errorMessage = strprintf("Error upon calling SetKey: %s\n", errorMessage); return false; } LogPrintf("CActiveMasternode::SendMasternodePing() - Relay Masternode Ping vin = %s\n", vin.ToString()); CMasternodePing mnp(vin); if(!mnp.Sign(keyMasternode, pubKeyMasternode)) { errorMessage = "Couldn't sign Masternode Ping"; return false; } // Update lastPing for our masternode in Masternode list CMasternode* pmn = mnodeman.Find(vin); if(pmn != NULL) { if(pmn->IsPingedWithin(MASTERNODE_PING_SECONDS, mnp.sigTime)){ errorMessage = "Too early to send Masternode Ping"; return false; } pmn->lastPing = mnp; mnodeman.mapSeenMasternodePing.insert(make_pair(mnp.GetHash(), mnp)); mnp.Relay(); /* * IT'S SAFE TO REMOVE THIS IN FURTHER VERSIONS * AFTER MIGRATION TO V12 IS DONE */ if(IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return true; // for migration purposes ping our node on old masternodes network too std::string retErrorMessage; std::vector vchMasterNodeSignature; int64_t masterNodeSignatureTime = GetAdjustedTime(); std::string strMessage = service.ToString() + boost::lexical_cast(masterNodeSignatureTime) + boost::lexical_cast(false); if(!darkSendSigner.SignMessage(strMessage, retErrorMessage, vchMasterNodeSignature, keyMasternode)) { errorMessage = "dseep sign message failed: " + retErrorMessage; return false; } if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchMasterNodeSignature, strMessage, retErrorMessage)) { errorMessage = "dseep verify message failed: " + retErrorMessage; return false; } LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) pnode->PushMessage("dseep", vin, vchMasterNodeSignature, masterNodeSignatureTime, false); /* * END OF "REMOVE" */ return true; } else { // Seems like we are trying to send a ping while the Masternode is not registered in the network errorMessage = "Darksend Masternode List doesn't include our Masternode, shutting down Masternode pinging service! " + vin.ToString(); status = ACTIVE_MASTERNODE_NOT_CAPABLE; notCapableReason = errorMessage; return false; } } bool CActiveMasternode::Register(std::string strService, std::string strKeyMasternode, std::string strTxHash, std::string strOutputIndex, std::string& errorMessage) { CTxIn vin; CPubKey pubKeyCollateralAddress; CKey keyCollateralAddress; CPubKey pubKeyMasternode; CKey keyMasternode; //need correct blocks to send ping if(!masternodeSync.IsBlockchainSynced()) { errorMessage = GetStatus(); LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); return false; } if(!darkSendSigner.SetKey(strKeyMasternode, errorMessage, keyMasternode, pubKeyMasternode)) { errorMessage = strprintf("Can't find keys for masternode %s - %s", strService, errorMessage); LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); return false; } if(!GetMasterNodeVin(vin, pubKeyCollateralAddress, keyCollateralAddress, strTxHash, strOutputIndex)) { errorMessage = strprintf("Could not allocate vin %s:%s for masternode %s", strTxHash, strOutputIndex, strService); LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); return false; } CService service = CService(strService); if(Params().NetworkID() == CBaseChainParams::MAIN) { if(service.GetPort() != 9999) { errorMessage = strprintf("Invalid port %u for masternode %s - only 9999 is supported on mainnet.", service.GetPort(), strService); LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); return false; } } else if(service.GetPort() == 9999) { errorMessage = strprintf("Invalid port %u for masternode %s - 9999 is only supported on mainnet.", service.GetPort(), strService); LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); return false; } return Register(vin, CService(strService), keyCollateralAddress, pubKeyCollateralAddress, keyMasternode, pubKeyMasternode, errorMessage); } bool CActiveMasternode::Register(CTxIn vin, CService service, CKey keyCollateralAddress, CPubKey pubKeyCollateralAddress, CKey keyMasternode, CPubKey pubKeyMasternode, std::string &errorMessage) { CMasternodeBroadcast mnb; CMasternodePing mnp(vin); if(!mnp.Sign(keyMasternode, pubKeyMasternode)){ errorMessage = strprintf("Failed to sign ping, vin: %s", vin.ToString()); LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); return false; } mnodeman.mapSeenMasternodePing.insert(make_pair(mnp.GetHash(), mnp)); LogPrintf("CActiveMasternode::Register() - Adding to Masternode list\n service: %s\n vin: %s\n", service.ToString(), vin.ToString()); mnb = CMasternodeBroadcast(service, vin, pubKeyCollateralAddress, pubKeyMasternode, PROTOCOL_VERSION); mnb.lastPing = mnp; if(!mnb.Sign(keyCollateralAddress)){ errorMessage = strprintf("Failed to sign broadcast, vin: %s", vin.ToString()); LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage); return false; } mnodeman.mapSeenMasternodeBroadcast.insert(make_pair(mnb.GetHash(), mnb)); masternodeSync.AddedMasternodeList(mnb.GetHash()); CMasternode* pmn = mnodeman.Find(vin); if(pmn == NULL) { CMasternode mn(mnb); mnodeman.Add(mn); } else { pmn->UpdateFromNewBroadcast(mnb); } //send to all peers LogPrintf("CActiveMasternode::Register() - RelayElectionEntry vin = %s\n", vin.ToString()); mnb.Relay(); /* * IT'S SAFE TO REMOVE THIS IN FURTHER VERSIONS * AFTER MIGRATION TO V12 IS DONE */ if(IsSporkActive(SPORK_10_MASTERNODE_PAY_UPDATED_NODES)) return true; // for migration purposes inject our node in old masternodes' list too std::string retErrorMessage; std::vector vchMasterNodeSignature; int64_t masterNodeSignatureTime = GetAdjustedTime(); std::string donationAddress = ""; int donationPercantage = 0; std::string vchPubKey(pubKeyCollateralAddress.begin(), pubKeyCollateralAddress.end()); std::string vchPubKey2(pubKeyMasternode.begin(), pubKeyMasternode.end()); std::string strMessage = service.ToString() + boost::lexical_cast(masterNodeSignatureTime) + vchPubKey + vchPubKey2 + boost::lexical_cast(PROTOCOL_VERSION) + donationAddress + boost::lexical_cast(donationPercantage); if(!darkSendSigner.SignMessage(strMessage, retErrorMessage, vchMasterNodeSignature, keyCollateralAddress)) { errorMessage = "dsee sign message failed: " + retErrorMessage; LogPrintf("CActiveMasternode::Register() - Error: %s\n", errorMessage.c_str()); return false; } if(!darkSendSigner.VerifyMessage(pubKeyCollateralAddress, vchMasterNodeSignature, strMessage, retErrorMessage)) { errorMessage = "dsee verify message failed: " + retErrorMessage; LogPrintf("CActiveMasternode::Register() - Error: %s\n", errorMessage.c_str()); return false; } LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) pnode->PushMessage("dsee", vin, service, vchMasterNodeSignature, masterNodeSignatureTime, pubKeyCollateralAddress, pubKeyMasternode, -1, -1, masterNodeSignatureTime, PROTOCOL_VERSION, donationAddress, donationPercantage); /* * END OF "REMOVE" */ return true; } bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey) { return GetMasterNodeVin(vin, pubkey, secretKey, "", ""); } bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey, std::string strTxHash, std::string strOutputIndex) { // Find possible candidates vector possibleCoins = SelectCoinsMasternode(); COutput *selectedOutput; // Find the vin if(!strTxHash.empty()) { // Let's find it uint256 txHash(strTxHash); int outputIndex = atoi(strOutputIndex.c_str()); bool found = false; BOOST_FOREACH(COutput& out, possibleCoins) { if(out.tx->GetHash() == txHash && out.i == outputIndex) { selectedOutput = &out; found = true; break; } } if(!found) { LogPrintf("CActiveMasternode::GetMasterNodeVin - Could not locate valid vin\n"); return false; } } else { // No output specified, Select the first one if(possibleCoins.size() > 0) { selectedOutput = &possibleCoins[0]; } else { LogPrintf("CActiveMasternode::GetMasterNodeVin - Could not locate specified vin from possible list\n"); return false; } } // At this point we have a selected output, retrieve the associated info return GetVinFromOutput(*selectedOutput, vin, pubkey, secretKey); } // Extract Masternode vin information from output bool CActiveMasternode::GetVinFromOutput(COutput out, CTxIn& vin, CPubKey& pubkey, CKey& secretKey) { CScript pubScript; vin = CTxIn(out.tx->GetHash(),out.i); pubScript = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey CTxDestination address1; ExtractDestination(pubScript, address1); CBitcoinAddress address2(address1); CKeyID keyID; if (!address2.GetKeyID(keyID)) { LogPrintf("CActiveMasternode::GetMasterNodeVin - Address does not refer to a key\n"); return false; } if (!pwalletMain->GetKey(keyID, secretKey)) { LogPrintf ("CActiveMasternode::GetMasterNodeVin - Private key for address is not known\n"); return false; } pubkey = secretKey.GetPubKey(); return true; } // get all possible outputs for running Masternode vector CActiveMasternode::SelectCoinsMasternode() { vector vCoins; vector filteredCoins; vector confLockedCoins; TRY_LOCK(pwalletMain->cs_wallet, fWallet); if(!fWallet) return filteredCoins; //if we can't get the lock, return nothing // Temporary unlock MN coins from masternode.conf if(GetBoolArg("-mnconflock", true)) { uint256 mnTxHash; BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) { mnTxHash.SetHex(mne.getTxHash()); COutPoint outpoint = COutPoint(mnTxHash, atoi(mne.getOutputIndex().c_str())); confLockedCoins.push_back(outpoint); pwalletMain->UnlockCoin(outpoint); } } // Retrieve all possible outputs pwalletMain->AvailableCoins(vCoins); // Lock MN coins from masternode.conf back if they where temporary unlocked if(!confLockedCoins.empty()) { BOOST_FOREACH(COutPoint outpoint, confLockedCoins) pwalletMain->LockCoin(outpoint); } // Filter BOOST_FOREACH(const COutput& out, vCoins) { if(out.tx->vout[out.i].nValue == 1000*COIN) { //exactly filteredCoins.push_back(out); } } return filteredCoins; } // when starting a Masternode, this can enable to run as a hot wallet with no funds bool CActiveMasternode::EnableHotColdMasterNode(CTxIn& newVin, CService& newService) { if(!fMasterNode) return false; status = ACTIVE_MASTERNODE_STARTED; //The values below are needed for signing mnping messages going forward vin = newVin; service = newService; LogPrintf("CActiveMasternode::EnableHotColdMasterNode() - Enabled! You may shut down the cold daemon.\n"); return true; }