activemasternode.cpp 17.9 KB
Newer Older
1

UdjinM6's avatar
fix    
UdjinM6 committed
2
#include "addrman.h"
3
4
#include "protocol.h"
#include "activemasternode.h"
5
#include "masternodeman.h"
Evan Duffield's avatar
Evan Duffield committed
6
#include "masternode.h"
7
#include "masternodeconfig.h"
UdjinM6's avatar
UdjinM6 committed
8
#include "spork.h"
9
10

//
Mike Kinney's avatar
Mike Kinney committed
11
// Bootup the Masternode, look for a 1000DRK input and register on the network
12
//
13
void CActiveMasternode::ManageStatus()
Evan Duffield's avatar
Evan Duffield committed
14
{    
15
16
    std::string errorMessage;

Evan Duffield's avatar
Evan Duffield committed
17
    if(!fMasterNode) return;
18

Evan Duffield's avatar
Evan Duffield committed
19
    if (fDebug) LogPrintf("CActiveMasternode::ManageStatus() - Begin\n");
20

21
    //need correct blocks to send ping
22
    if(Params().NetworkID() != CBaseChainParams::REGTEST && !masternodeSync.IsBlockchainSynced()) {
23
        status = ACTIVE_MASTERNODE_SYNC_IN_PROCESS;
UdjinM6's avatar
UdjinM6 committed
24
        LogPrintf("CActiveMasternode::ManageStatus() - %s\n", GetStatus());
25
26
27
        return;
    }

UdjinM6's avatar
UdjinM6 committed
28
29
30
    if(status == ACTIVE_MASTERNODE_SYNC_IN_PROCESS) status = ACTIVE_MASTERNODE_INITIAL;

    if(status == ACTIVE_MASTERNODE_INITIAL) {
31
32
33
34
        CMasternode *pmn;
        pmn = mnodeman.Find(pubKeyMasternode);
        if(pmn != NULL) {
            pmn->Check();
35
            if(pmn->IsEnabled() && pmn->protocolVersion == PROTOCOL_VERSION) EnableHotColdMasterNode(pmn->vin, pmn->addr);
36
        }
37
38
    }

39
    if(status != ACTIVE_MASTERNODE_STARTED) {
40
41

        // Set defaults
42
        status = ACTIVE_MASTERNODE_NOT_CAPABLE;
43
44
        notCapableReason = "";

45
46
47
48
49
50
51
52
53
54
55
56
        if(pwalletMain->IsLocked()){
            notCapableReason = "Wallet is locked.";
            LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason);
            return;
        }

        if(pwalletMain->GetBalance() == 0){
            notCapableReason = "Hot node, waiting for remote activation.";
            LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason);
            return;
        }

57
        if(strMasterNodeAddr.empty()) {
58
            if(!GetLocal(service)) {
UdjinM6's avatar
UdjinM6 committed
59
                notCapableReason = "Can't detect external address. Please use the masternodeaddr configuration option.";
60
                LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason);
61
62
63
                return;
            }
        } else {
UdjinM6's avatar
UdjinM6 committed
64
            service = CService(strMasterNodeAddr);
65
66
        }

67
        LogPrintf("CActiveMasternode::ManageStatus() - Checking inbound connection to '%s'\n", service.ToString());
68

69
        if(Params().NetworkID() == CBaseChainParams::MAIN) {
70
            if(service.GetPort() != 9999) {
UdjinM6's avatar
UdjinM6 committed
71
                notCapableReason = strprintf("Invalid port: %u - only 9999 is supported on mainnet.", service.GetPort());
72
                LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason);
73
74
                return;
            }
UdjinM6's avatar
UdjinM6 committed
75
        } else if(service.GetPort() == 9999) {
UdjinM6's avatar
UdjinM6 committed
76
            notCapableReason = strprintf("Invalid port: %u - 9999 is only supported on mainnet.", service.GetPort());
77
            LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason);
UdjinM6's avatar
UdjinM6 committed
78
            return;
79
80
        }

81
82
        if(!ConnectNode((CAddress)service, service.ToString().c_str())){
            notCapableReason = "Could not connect to " + service.ToString();
83
            LogPrintf("CActiveMasternode::ManageStatus() - not capable: %s\n", notCapableReason);
84
            return;
85
86
87
        }

        // Choose coins to use
88
89
        CPubKey pubKeyCollateralAddress;
        CKey keyCollateralAddress;
90

91
        if(GetMasterNodeVin(vin, pubKeyCollateralAddress, keyCollateralAddress)) {
92

93
            if(GetInputAge(vin) < MASTERNODE_MIN_CONFIRMATIONS){
94
                status = ACTIVE_MASTERNODE_INPUT_TOO_NEW;
UdjinM6's avatar
UdjinM6 committed
95
96
                notCapableReason = strprintf("%s - %d confirmations", GetStatus(), GetInputAge(vin));
                LogPrintf("CActiveMasternode::ManageStatus() - %s\n", notCapableReason);
97
98
99
                return;
            }

UdjinM6's avatar
UdjinM6 committed
100
            LOCK(pwalletMain->cs_wallet);
101
            pwalletMain->LockCoin(vin.prevout);
102

103
104
105
            // send to all nodes
            CPubKey pubKeyMasternode;
            CKey keyMasternode;
106

107
108
            if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode))
            {
109
110
                notCapableReason = "Error upon calling SetKey: " + errorMessage;
                LogPrintf("Register::ManageStatus() - %s\n", notCapableReason);
UdjinM6's avatar
UdjinM6 committed
111
                return;
112
113
            }

114
            if(!Register(vin, service, keyCollateralAddress, pubKeyCollateralAddress, keyMasternode, pubKeyMasternode, errorMessage)) {
115
116
117
                notCapableReason = "Error on Register: " + errorMessage;
                LogPrintf("Register::ManageStatus() - %s\n", notCapableReason);
                return;
118
119
            }

120
            LogPrintf("CActiveMasternode::ManageStatus() - Is capable master node!\n");
121
            status = ACTIVE_MASTERNODE_STARTED;
122

123
            return;
124
        } else {
125
            notCapableReason = "Could not find suitable coins!";
126
127
            LogPrintf("CActiveMasternode::ManageStatus() - %s\n", notCapableReason);
            return;
128
129
130
        }
    }

131
    //send to all peers
132
133
134
135
136
137
138
    if(!SendMasternodePing(errorMessage)) {
        LogPrintf("CActiveMasternode::ManageStatus() - Error on Ping: %s\n", errorMessage);
    }
}

std::string CActiveMasternode::GetStatus() {
    switch (status) {
UdjinM6's avatar
UdjinM6 committed
139
140
141
142
143
    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";
144
    default: return "unknown";
145
146
    }
}
147

148
bool CActiveMasternode::SendMasternodePing(std::string& errorMessage) {
149
    if(status != ACTIVE_MASTERNODE_STARTED) {
UdjinM6's avatar
UdjinM6 committed
150
151
152
        errorMessage = "Masternode is not in a running status";
        return false;
    }
153

154
155
156
157
158
    CPubKey pubKeyMasternode;
    CKey keyMasternode;

    if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode))
    {
159
        errorMessage = strprintf("Error upon calling SetKey: %s\n", errorMessage);
UdjinM6's avatar
UdjinM6 committed
160
        return false;
161
162
    }

163
    LogPrintf("CActiveMasternode::SendMasternodePing() - Relay Masternode Ping vin = %s\n", vin.ToString());
Evan Duffield's avatar
Evan Duffield committed
164
165
166
    
    CMasternodePing mnp(vin);
    if(!mnp.Sign(keyMasternode, pubKeyMasternode))
167
    {
168
        errorMessage = "Couldn't sign Masternode Ping";
169
170
171
        return false;
    }

172
    // Update lastPing for our masternode in Masternode list
UdjinM6's avatar
UdjinM6 committed
173
174
    CMasternode* pmn = mnodeman.Find(vin);
    if(pmn != NULL)
175
    {
176
177
178
179
180
181
        if(pmn->IsPingedWithin(MASTERNODE_PING_SECONDS, mnp.sigTime)){
            errorMessage = "Too early to send Masternode Ping";
            return false;
        }

        pmn->lastPing = mnp;
UdjinM6's avatar
UdjinM6 committed
182
        mnodeman.mapSeenMasternodePing.insert(make_pair(mnp.GetHash(), mnp));
183
        mnp.Relay();
184

UdjinM6's avatar
UdjinM6 committed
185
186
187
188
189
190
191
192
193
194
195
        /*
         * 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<unsigned char> vchMasterNodeSignature;
        int64_t masterNodeSignatureTime = GetAdjustedTime();

UdjinM6's avatar
UdjinM6 committed
196
        std::string strMessage = service.ToString() + boost::lexical_cast<std::string>(masterNodeSignatureTime) + boost::lexical_cast<std::string>(false);
UdjinM6's avatar
UdjinM6 committed
197
198
199
200
201
202
203
204
205
206
207

        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;
        }

UdjinM6's avatar
UdjinM6 committed
208
        LogPrint("masternode", "dseep - relaying from active mn, %s \n", vin.ToString().c_str());
UdjinM6's avatar
UdjinM6 committed
209
210
        LOCK(cs_vNodes);
        BOOST_FOREACH(CNode* pnode, vNodes)
UdjinM6's avatar
UdjinM6 committed
211
            pnode->PushMessage("dseep", vin, vchMasterNodeSignature, masterNodeSignatureTime, false);
UdjinM6's avatar
UdjinM6 committed
212
213
214
215
216

        /*
         * END OF "REMOVE"
         */

217
        return true;
218
    }
219
220
    else
    {
UdjinM6's avatar
UdjinM6 committed
221
        // Seems like we are trying to send a ping while the Masternode is not registered in the network
222
        errorMessage = "Darksend Masternode List doesn't include our Masternode, shutting down Masternode pinging service! " + vin.ToString();
223
        status = ACTIVE_MASTERNODE_NOT_CAPABLE;
224
        notCapableReason = errorMessage;
225
226
227
        return false;
    }

228
}
229

UdjinM6's avatar
UdjinM6 committed
230
bool CActiveMasternode::Register(std::string strService, std::string strKeyMasternode, std::string strTxHash, std::string strOutputIndex, std::string& errorMessage) {
UdjinM6's avatar
UdjinM6 committed
231
    CTxIn vin;
232
233
234
235
236
    CPubKey pubKeyCollateralAddress;
    CKey keyCollateralAddress;
    CPubKey pubKeyMasternode;
    CKey keyMasternode;

UdjinM6's avatar
UdjinM6 committed
237
    //need correct blocks to send ping
238
    if(!masternodeSync.IsBlockchainSynced()) {
UdjinM6's avatar
UdjinM6 committed
239
        errorMessage = GetStatus();
UdjinM6's avatar
UdjinM6 committed
240
241
242
243
        LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage);
        return false;
    }

244
245
    if(!darkSendSigner.SetKey(strKeyMasternode, errorMessage, keyMasternode, pubKeyMasternode))
    {
UdjinM6's avatar
UdjinM6 committed
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
        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);
UdjinM6's avatar
UdjinM6 committed
267
        return false;
268
269
    }

UdjinM6's avatar
fix    
UdjinM6 committed
270
271
    addrman.Add(CAddress(service), CNetAddr("127.0.0.1"), 2*60*60);

272
    return Register(vin, CService(strService), keyCollateralAddress, pubKeyCollateralAddress, keyMasternode, pubKeyMasternode, errorMessage);
273
}
274

UdjinM6's avatar
UdjinM6 committed
275
bool CActiveMasternode::Register(CTxIn vin, CService service, CKey keyCollateralAddress, CPubKey pubKeyCollateralAddress, CKey keyMasternode, CPubKey pubKeyMasternode, std::string &errorMessage) {
276
277
278
    CMasternodeBroadcast mnb;
    CMasternodePing mnp(vin);
    if(!mnp.Sign(keyMasternode, pubKeyMasternode)){
UdjinM6's avatar
UdjinM6 committed
279
280
        errorMessage = strprintf("Failed to sign ping, vin: %s", vin.ToString());
        LogPrintf("CActiveMasternode::Register() -  %s\n", errorMessage);
281
282
        return false;
    }
UdjinM6's avatar
UdjinM6 committed
283
    mnodeman.mapSeenMasternodePing.insert(make_pair(mnp.GetHash(), mnp));
284

UdjinM6's avatar
UdjinM6 committed
285
    LogPrintf("CActiveMasternode::Register() - Adding to Masternode list\n    service: %s\n    vin: %s\n", service.ToString(), vin.ToString());
286
287
288
    mnb = CMasternodeBroadcast(service, vin, pubKeyCollateralAddress, pubKeyMasternode, PROTOCOL_VERSION);
    mnb.lastPing = mnp;
    if(!mnb.Sign(keyCollateralAddress)){
UdjinM6's avatar
UdjinM6 committed
289
290
        errorMessage = strprintf("Failed to sign broadcast, vin: %s", vin.ToString());
        LogPrintf("CActiveMasternode::Register() - %s\n", errorMessage);
291
292
        return false;
    }
UdjinM6's avatar
UdjinM6 committed
293
    mnodeman.mapSeenMasternodeBroadcast.insert(make_pair(mnb.GetHash(), mnb));
294
    masternodeSync.AddedMasternodeList(mnb.GetHash());
295

UdjinM6's avatar
UdjinM6 committed
296
297
    CMasternode* pmn = mnodeman.Find(vin);
    if(pmn == NULL)
298
    {
Evan Duffield's avatar
Evan Duffield committed
299
        CMasternode mn(mnb);
300
        mnodeman.Add(mn);
301
302
    } else {
        pmn->UpdateFromNewBroadcast(mnb);
303
    }
304

305
306
307
    //send to all peers
    LogPrintf("CActiveMasternode::Register() - RelayElectionEntry vin = %s\n", vin.ToString());
    mnb.Relay();
308

UdjinM6's avatar
UdjinM6 committed
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
    /*
     * 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<unsigned char> 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<std::string>(masterNodeSignatureTime) + vchPubKey + vchPubKey2 + boost::lexical_cast<std::string>(PROTOCOL_VERSION) + donationAddress + boost::lexical_cast<std::string>(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"
     */

347
348
349
350
    return true;
}

bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey) {
UdjinM6's avatar
UdjinM6 committed
351
    return GetMasterNodeVin(vin, pubkey, secretKey, "", "");
352
}
353

354
355
bool CActiveMasternode::GetMasterNodeVin(CTxIn& vin, CPubKey& pubkey, CKey& secretKey, std::string strTxHash, std::string strOutputIndex) {
    // Find possible candidates
Evan Duffield's avatar
Evan Duffield committed
356
357
358
    TRY_LOCK(pwalletMain->cs_wallet, fWallet);
    if(!fWallet) return false;

359
360
361
362
    vector<COutput> possibleCoins = SelectCoinsMasternode();
    COutput *selectedOutput;

    // Find the vin
UdjinM6's avatar
UdjinM6 committed
363
364
365
    if(!strTxHash.empty()) {
        // Let's find it
        uint256 txHash(strTxHash);
UdjinM6's avatar
UdjinM6 committed
366
        int outputIndex = atoi(strOutputIndex.c_str());
UdjinM6's avatar
UdjinM6 committed
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
        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;
        }
388
389
    }

UdjinM6's avatar
UdjinM6 committed
390
391
    // At this point we have a selected output, retrieve the associated info
    return GetVinFromOutput(*selectedOutput, vin, pubkey, secretKey);
392
393
394
}


Mike Kinney's avatar
Mike Kinney committed
395
// Extract Masternode vin information from output
396
397
bool CActiveMasternode::GetVinFromOutput(COutput out, CTxIn& vin, CPubKey& pubkey, CKey& secretKey) {

398
399
    CScript pubScript;

UdjinM6's avatar
UdjinM6 committed
400
    vin = CTxIn(out.tx->GetHash(),out.i);
401
    pubScript = out.tx->vout[out.i].scriptPubKey; // the inputs PubKey
402

UdjinM6's avatar
UdjinM6 committed
403
    CTxDestination address1;
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
    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;
}

Mike Kinney's avatar
Mike Kinney committed
422
// get all possible outputs for running Masternode
423
424
425
426
vector<COutput> CActiveMasternode::SelectCoinsMasternode()
{
    vector<COutput> vCoins;
    vector<COutput> filteredCoins;
427
428
429
430
431
432
433
    vector<COutPoint> confLockedCoins;

    // Temporary unlock MN coins from masternode.conf
    if(GetBoolArg("-mnconflock", true)) {
        uint256 mnTxHash;
        BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
            mnTxHash.SetHex(mne.getTxHash());
UdjinM6's avatar
UdjinM6 committed
434
            COutPoint outpoint = COutPoint(mnTxHash, atoi(mne.getOutputIndex().c_str()));
435
436
437
438
            confLockedCoins.push_back(outpoint);
            pwalletMain->UnlockCoin(outpoint);
        }
    }
439
440

    // Retrieve all possible outputs
441
    pwalletMain->AvailableCoins(vCoins);
442

443
444
445
446
447
448
    // Lock MN coins from masternode.conf back if they where temporary unlocked
    if(!confLockedCoins.empty()) {
        BOOST_FOREACH(COutPoint outpoint, confLockedCoins)
            pwalletMain->LockCoin(outpoint);
    }

449
450
451
452
    // Filter
    BOOST_FOREACH(const COutput& out, vCoins)
    {
        if(out.tx->vout[out.i].nValue == 1000*COIN) { //exactly
UdjinM6's avatar
UdjinM6 committed
453
            filteredCoins.push_back(out);
454
455
456
457
458
        }
    }
    return filteredCoins;
}

Mike Kinney's avatar
Mike Kinney committed
459
// when starting a Masternode, this can enable to run as a hot wallet with no funds
460
bool CActiveMasternode::EnableHotColdMasterNode(CTxIn& newVin, CService& newService)
461
462
463
{
    if(!fMasterNode) return false;

464
    status = ACTIVE_MASTERNODE_STARTED;
465

Evan Duffield's avatar
Evan Duffield committed
466
    //The values below are needed for signing mnping messages going forward
UdjinM6's avatar
UdjinM6 committed
467
468
    vin = newVin;
    service = newService;
469
470
471
472
473

    LogPrintf("CActiveMasternode::EnableHotColdMasterNode() - Enabled! You may shut down the cold daemon.\n");

    return true;
}