masternode-budget.cpp 59.4 KB
Newer Older
1
2
3
4
// Copyright (c) 2014-2015 The Dash developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

5
6
7
#include "main.h"
#include "init.h"

8
9
10
11
#include "masternode-budget.h"
#include "masternode.h"
#include "darksend.h"
#include "masternodeman.h"
12
#include "masternode-sync.h"
13
14
15
16
17
18
19
20
#include "util.h"
#include "addrman.h"
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>

CBudgetManager budget;
CCriticalSection cs_budget;

21
22
std::map<uint256, int64_t> askedForSourceProposalOrBudget;

23
int nSubmittedFinalBudget;
24

25
int GetBudgetPaymentCycleBlocks(){
UdjinM6's avatar
UdjinM6 committed
26
27
    // Amount of blocks in a months period of time (using 2.6 minutes per) = (60*24*30)/2.6
    if(Params().NetworkID() == CBaseChainParams::MAIN) return 16616;
28
    //for testing purposes
Evan Duffield's avatar
Evan Duffield committed
29

30
31
32
    return 50;
}

33
bool IsBudgetCollateralValid(uint256 nTxCollateralHash, uint256 nExpectedHash, std::string& strError)
34
{
Evan Duffield's avatar
Evan Duffield committed
35
    CTransaction txCollateral;
36
37
    uint256 nBlockHash;
    if(!GetTransaction(nTxCollateralHash, txCollateral, nBlockHash, true)){
38
39
        strError = strprintf("Can't find collateral tx %s", txCollateral.ToString());
        LogPrintf ("CBudgetProposalBroadcast::IsBudgetCollateralValid - %s\n", strError);
Evan Duffield's avatar
Evan Duffield committed
40
        return false;
41
    }
42

Evan Duffield's avatar
Evan Duffield committed
43
44
    if(txCollateral.vout.size() < 1) return false;
    if(txCollateral.nLockTime != 0) return false;
45

46
47
    CScript findScript;
    findScript << OP_RETURN << ToByteVector(nExpectedHash);
48

49
    bool foundOpReturn = false;
Evan Duffield's avatar
Evan Duffield committed
50
    BOOST_FOREACH(const CTxOut o, txCollateral.vout){
51
        if(!o.scriptPubKey.IsNormalPaymentScript() && !o.scriptPubKey.IsUnspendable()){
52
53
            strError = strprintf("Invalid Script %s", txCollateral.ToString());
            LogPrintf ("CBudgetProposalBroadcast::IsBudgetCollateralValid - %s\n", strError);
Evan Duffield's avatar
Evan Duffield committed
54
55
            return false;
        }
56
        if(o.scriptPubKey == findScript && o.nValue >= BUDGET_FEE_TX) foundOpReturn = true;
57
58

    }
Evan Duffield's avatar
Evan Duffield committed
59
    if(!foundOpReturn){
60
        strError = strprintf("Couldn't find opReturn %s in %s", nExpectedHash.ToString(), txCollateral.ToString());
61
        LogPrintf ("CBudgetProposalBroadcast::IsBudgetCollateralValid - %s\n", strError);
62
63
64
        return false;
    }

65
    int conf = GetIXConfirmations(nTxCollateralHash);
UdjinM6's avatar
UdjinM6 committed
66
    if (nBlockHash != uint256(0)) {
67
68
69
70
        BlockMap::iterator mi = mapBlockIndex.find(nBlockHash);
        if (mi != mapBlockIndex.end() && (*mi).second) {
            CBlockIndex* pindex = (*mi).second;
            if (chainActive.Contains(pindex)) {
UdjinM6's avatar
UdjinM6 committed
71
                conf += chainActive.Height() - pindex->nHeight + 1;
72
73
74
75
            }
        }
    }

Evan Duffield's avatar
Evan Duffield committed
76
77
78
79
    //if we're syncing we won't have instantX information, so accept 1 confirmation 
    if(conf >= BUDGET_FEE_CONFIRMATIONS || (!masternodeSync.IsSynced() && conf >= 1)){
        return true;
    } else {
80
81
        strError = strprintf("Collateral requires at least %d confirmations - %d confirmations", BUDGET_FEE_CONFIRMATIONS, conf);
        LogPrintf ("CBudgetProposalBroadcast::IsBudgetCollateralValid - %s - %d confirmations\n", strError, conf);
Evan Duffield's avatar
Evan Duffield committed
82
83
        return false;
    }
84
85
}

Evan Duffield's avatar
Evan Duffield committed
86
void CBudgetManager::CheckOrphanVotes()
87
{
88
    std::string strError = "";
Evan Duffield's avatar
Evan Duffield committed
89
90
    std::map<uint256, CBudgetVote>::iterator it1 = mapOrphanMasternodeBudgetVotes.begin();
    while(it1 != mapOrphanMasternodeBudgetVotes.end()){
91
        if(budget.UpdateProposal(((*it1).second), NULL, strError)){
UdjinM6's avatar
UdjinM6 committed
92
            LogPrintf("CBudgetManager::CheckOrphanVotes - Proposal/Budget is known, activating and removing orphan vote\n");
Evan Duffield's avatar
Evan Duffield committed
93
94
95
            mapOrphanMasternodeBudgetVotes.erase(it1++);
        } else {
            ++it1;
96
97
        }
    }
Evan Duffield's avatar
Evan Duffield committed
98
99
    std::map<uint256, CFinalizedBudgetVote>::iterator it2 = mapOrphanFinalizedBudgetVotes.begin();
    while(it2 != mapOrphanFinalizedBudgetVotes.end()){
100
        if(budget.UpdateFinalizedBudget(((*it2).second),NULL, strError)){
UdjinM6's avatar
UdjinM6 committed
101
            LogPrintf("CBudgetManager::CheckOrphanVotes - Proposal/Budget is known, activating and removing orphan vote\n");
Evan Duffield's avatar
Evan Duffield committed
102
103
104
            mapOrphanFinalizedBudgetVotes.erase(it2++);
        } else {
            ++it2;
105
106
107
108
        }
    }
}

109
void CBudgetManager::SubmitFinalBudget()
110
111
112
113
{
    CBlockIndex* pindexPrev = chainActive.Tip();
    if(!pindexPrev) return;

UdjinM6's avatar
UdjinM6 committed
114
    int nBlockStart = pindexPrev->nHeight - pindexPrev->nHeight % GetBudgetPaymentCycleBlocks() + GetBudgetPaymentCycleBlocks();
115
    if(nSubmittedFinalBudget >= nBlockStart) return;
116
    if(nBlockStart - pindexPrev->nHeight > 576*2) return; //submit final budget 2 days before payment
117

118
    std::vector<CBudgetProposal*> vBudgetProposals = budget.GetBudget();
119
    std::string strBudgetName = "main";
120
    std::vector<CTxBudgetPayment> vecTxBudgetPayments;
121

122
    for(unsigned int i = 0; i < vBudgetProposals.size(); i++){
UdjinM6's avatar
UdjinM6 committed
123
124
125
        CTxBudgetPayment txBudgetPayment;
        txBudgetPayment.nProposalHash = vBudgetProposals[i]->GetHash();
        txBudgetPayment.payee = vBudgetProposals[i]->GetPayee();
Evan Duffield's avatar
Evan Duffield committed
126
        txBudgetPayment.nAmount = vBudgetProposals[i]->GetAllotted();
UdjinM6's avatar
UdjinM6 committed
127
        vecTxBudgetPayments.push_back(txBudgetPayment);
128
129
    }

130
    if(vecTxBudgetPayments.size() < 1) {
UdjinM6's avatar
UdjinM6 committed
131
        LogPrintf("CBudgetManager::SubmitFinalBudget - Found No Proposals For Period\n");
Evan Duffield's avatar
Evan Duffield committed
132
133
        return;
    }
134

Evan Duffield's avatar
Evan Duffield committed
135
    CFinalizedBudgetBroadcast tempBudget(strBudgetName, nBlockStart, vecTxBudgetPayments, 0);
136
137
138
139
140
    if(mapSeenFinalizedBudgets.count(tempBudget.GetHash())) {
        LogPrintf("CBudgetManager::SubmitFinalBudget - Budget already exists - %s\n", tempBudget.GetHash().ToString());    
        nSubmittedFinalBudget = pindexPrev->nHeight;
        return; //already exists
    }
Evan Duffield's avatar
Evan Duffield committed
141

Evan Duffield's avatar
Evan Duffield committed
142
    //create fee tx
Evan Duffield's avatar
Evan Duffield committed
143
    CTransaction tx;
144
    if(!mapCollateral.count(tempBudget.GetHash())){
145
146
        CWalletTx wtx;
        if(!pwalletMain->GetBudgetSystemCollateralTX(wtx, tempBudget.GetHash(), true)){
UdjinM6's avatar
UdjinM6 committed
147
            LogPrintf("CBudgetManager::SubmitFinalBudget - Can't make collateral transaction\n");
148
149
            return;
        }
150
151
152
153
154
155
156
157
        
        // make our change address
        CReserveKey reservekey(pwalletMain);
        //send the tx to the network
        pwalletMain->CommitTransaction(wtx, reservekey, "ix");

        mapCollateral.insert(make_pair(tempBudget.GetHash(), (CTransaction)wtx));
        tx = (CTransaction)wtx;
158
159
160
161
162
163
    } else {
        tx = mapCollateral[tempBudget.GetHash()];
    }

    CTxIn in(COutPoint(tx.GetHash(), 0));
    int conf = GetInputAgeIX(tx.GetHash(), in);
164
165
166
167
168
    /*
        Wait will we have 1 extra confirmation, otherwise some clients might reject this feeTX
        -- This function is tied to NewBlock, so we will propagate this budget while the block is also propagating
    */
    if(conf < BUDGET_FEE_CONFIRMATIONS+1){
UdjinM6's avatar
UdjinM6 committed
169
        LogPrintf ("CBudgetManager::SubmitFinalBudget - Collateral requires at least %d confirmations - %s - %d confirmations\n", BUDGET_FEE_CONFIRMATIONS, tx.GetHash().ToString(), conf);
Evan Duffield's avatar
Evan Duffield committed
170
171
        return;
    }
Evan Duffield's avatar
Evan Duffield committed
172

173
174
    nSubmittedFinalBudget = nBlockStart;

175
    //create the proposal incase we're the first to make it
Evan Duffield's avatar
Evan Duffield committed
176
    CFinalizedBudgetBroadcast finalizedBudgetBroadcast(strBudgetName, nBlockStart, vecTxBudgetPayments, tx.GetHash());
177

Evan Duffield's avatar
Evan Duffield committed
178
179
    std::string strError = "";
    if(!finalizedBudgetBroadcast.IsValid(strError)){
UdjinM6's avatar
UdjinM6 committed
180
        LogPrintf("CBudgetManager::SubmitFinalBudget - Invalid finalized budget - %s \n", strError);
Evan Duffield's avatar
Evan Duffield committed
181
        return;
182
183
    }

184
185
186
    mapSeenFinalizedBudgets.insert(make_pair(finalizedBudgetBroadcast.GetHash(), finalizedBudgetBroadcast));
    finalizedBudgetBroadcast.Relay();
    budget.AddFinalizedBudget(finalizedBudgetBroadcast);
187
188
}

189
190
191
192
193
194
195
196
197
198
//
// CBudgetDB
//

CBudgetDB::CBudgetDB()
{
    pathDB = GetDataDir() / "budget.dat";
    strMagicMessage = "MasternodeBudget";
}

199
bool CBudgetDB::Write(const CBudgetManager& objToSave)
200
201
202
203
{
    int64_t nStart = GetTimeMillis();

    // serialize, checksum data up to that point, then append checksum
204
205
206
207
208
209
    CDataStream ssObj(SER_DISK, CLIENT_VERSION);
    ssObj << strMagicMessage; // masternode cache file specific magic message
    ssObj << FLATDATA(Params().MessageStart()); // network specific magic number
    ssObj << objToSave;
    uint256 hash = Hash(ssObj.begin(), ssObj.end());
    ssObj << hash;
210
211
212
213
214
215
216
217
218

    // open output file, and associate with CAutoFile
    FILE *file = fopen(pathDB.string().c_str(), "wb");
    CAutoFile fileout(file, SER_DISK, CLIENT_VERSION);
    if (fileout.IsNull())
        return error("%s : Failed to open file %s", __func__, pathDB.string());

    // Write and commit header, data
    try {
219
        fileout << ssObj;
220
221
222
223
224
225
    }
    catch (std::exception &e) {
        return error("%s : Serialize or I/O error - %s", __func__, e.what());
    }
    fileout.fclose();

226
    LogPrintf("Written info to budget.dat  %dms\n", GetTimeMillis() - nStart);
227
228
229
230

    return true;
}

UdjinM6's avatar
UdjinM6 committed
231
CBudgetDB::ReadResult CBudgetDB::Read(CBudgetManager& objToLoad, bool fDryRun)
232
{
233

234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
    int64_t nStart = GetTimeMillis();
    // open input file, and associate with CAutoFile
    FILE *file = fopen(pathDB.string().c_str(), "rb");
    CAutoFile filein(file, SER_DISK, CLIENT_VERSION);
    if (filein.IsNull())
    {
        error("%s : Failed to open file %s", __func__, pathDB.string());
        return FileError;
    }

    // use file size to size memory buffer
    int fileSize = boost::filesystem::file_size(pathDB);
    int dataSize = fileSize - sizeof(uint256);
    // Don't try to resize to a negative number if file is small
    if (dataSize < 0)
        dataSize = 0;
    vector<unsigned char> vchData;
    vchData.resize(dataSize);
    uint256 hashIn;

    // read data and checksum from file
    try {
        filein.read((char *)&vchData[0], dataSize);
        filein >> hashIn;
    }
    catch (std::exception &e) {
        error("%s : Deserialize or I/O error - %s", __func__, e.what());
        return HashReadError;
    }
    filein.fclose();

265
    CDataStream ssObj(vchData, SER_DISK, CLIENT_VERSION);
266
267

    // verify stored checksum matches input data
268
    uint256 hashTmp = Hash(ssObj.begin(), ssObj.end());
269
270
271
272
273
274
    if (hashIn != hashTmp)
    {
        error("%s : Checksum mismatch, data corrupted", __func__);
        return IncorrectHash;
    }

275

276
277
278
279
    unsigned char pchMsgTmp[4];
    std::string strMagicMessageTmp;
    try {
        // de-serialize file header (masternode cache file specific magic message) and ..
280
        ssObj >> strMagicMessageTmp;
281
282
283
284
285
286
287
288
289
290

        // ... verify the message matches predefined one
        if (strMagicMessage != strMagicMessageTmp)
        {
            error("%s : Invalid masternode cache magic message", __func__);
            return IncorrectMagicMessage;
        }


        // de-serialize file header (network specific magic number) and ..
291
        ssObj >> FLATDATA(pchMsgTmp);
292
293
294
295
296
297
298
299
300

        // ... verify the network matches ours
        if (memcmp(pchMsgTmp, Params().MessageStart(), sizeof(pchMsgTmp)))
        {
            error("%s : Invalid network magic number", __func__);
            return IncorrectMagicNumber;
        }

        // de-serialize data into CBudgetManager object
301
        ssObj >> objToLoad;
302
303
    }
    catch (std::exception &e) {
304
        objToLoad.Clear();
305
306
307
308
        error("%s : Deserialize or I/O error - %s", __func__, e.what());
        return IncorrectFormat;
    }

309
    LogPrintf("Loaded info from budget.dat  %dms\n", GetTimeMillis() - nStart);
310
    LogPrintf("  %s\n", objToLoad.ToString());
UdjinM6's avatar
UdjinM6 committed
311
312
313
314
315
316
    if(!fDryRun) {
        LogPrintf("Budget manager - cleaning....\n");
        objToLoad.CheckAndRemove();
        LogPrintf("Budget manager - result:\n");
        LogPrintf("  %s\n", objToLoad.ToString());
    }
317
318
319
320
321
322
323
324

    return Ok;
}

void DumpBudgets()
{
    int64_t nStart = GetTimeMillis();

UdjinM6's avatar
UdjinM6 committed
325
326
    CBudgetDB budgetdb;
    CBudgetManager tempBudget;
327

UdjinM6's avatar
UdjinM6 committed
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
    LogPrintf("Verifying budget.dat format...\n");
    CBudgetDB::ReadResult readResult = budgetdb.Read(tempBudget, true);
    // there was an error and it was not an error on file opening => do not proceed
    if (readResult == CBudgetDB::FileError)
        LogPrintf("Missing budgets file - budget.dat, will try to recreate\n");
    else if (readResult != CBudgetDB::Ok)
    {
        LogPrintf("Error reading budget.dat: ");
        if(readResult == CBudgetDB::IncorrectFormat)
            LogPrintf("magic is ok but data has invalid format, will try to recreate\n");
        else
        {
            LogPrintf("file format is unknown or invalid, please fix it manually\n");
            return;
        }
    }
344
    LogPrintf("Writting info to budget.dat...\n");
UdjinM6's avatar
UdjinM6 committed
345
    budgetdb.Write(budget);
346

347
    LogPrintf("Budget dump finished  %dms\n", GetTimeMillis() - nStart);
348
349
}

Evan Duffield's avatar
Evan Duffield committed
350
bool CBudgetManager::AddFinalizedBudget(CFinalizedBudget& finalizedBudget)
Evan Duffield's avatar
draft    
Evan Duffield committed
351
{
352
    LOCK(cs);
Evan Duffield's avatar
Evan Duffield committed
353
    std::string strError = "";
Evan Duffield's avatar
Evan Duffield committed
354
    if(!finalizedBudget.IsValid(strError)) return false;
355

Evan Duffield's avatar
Evan Duffield committed
356
    if(mapFinalizedBudgets.count(finalizedBudget.GetHash())) {
Evan Duffield's avatar
Evan Duffield committed
357
        return false;
358
    }
359

360
    mapFinalizedBudgets.insert(make_pair(finalizedBudget.GetHash(), finalizedBudget));
Evan Duffield's avatar
Evan Duffield committed
361
    return true;
Evan Duffield's avatar
draft    
Evan Duffield committed
362
363
}

Evan Duffield's avatar
Evan Duffield committed
364
bool CBudgetManager::AddProposal(CBudgetProposal& budgetProposal)
365
{
366
    LOCK(cs);
367
368
    std::string strError = "";
    if(!budgetProposal.IsValid(strError)) {
UdjinM6's avatar
UdjinM6 committed
369
        LogPrintf("CBudgetManager::AddProposal - invalid budget proposal - %s\n", strError);
Evan Duffield's avatar
Evan Duffield committed
370
        return false;
371
372
    }

Evan Duffield's avatar
Evan Duffield committed
373
    if(mapProposals.count(budgetProposal.GetHash())) {
Evan Duffield's avatar
Evan Duffield committed
374
        return false;
375
    }
376

377
    mapProposals.insert(make_pair(budgetProposal.GetHash(), budgetProposal));
Evan Duffield's avatar
Evan Duffield committed
378
    return true;
379
380
}

381
void CBudgetManager::CheckAndRemove()
382
{
Evan Duffield's avatar
Evan Duffield committed
383
    std::string strError = "";
384
385
386
    std::map<uint256, CFinalizedBudget>::iterator it = mapFinalizedBudgets.begin();
    while(it != mapFinalizedBudgets.end())
    {
387
        CFinalizedBudget* pfinalizedBudget = &((*it).second);
UdjinM6's avatar
UdjinM6 committed
388
389
390

        pfinalizedBudget->fValid = pfinalizedBudget->IsValid(strError);
        if(pfinalizedBudget->fValid) {
391
            pfinalizedBudget->AutoCheck();
392
        }
UdjinM6's avatar
UdjinM6 committed
393

394
        ++it;
395
    }
396

397
398
399
    std::map<uint256, CBudgetProposal>::iterator it2 = mapProposals.begin();
    while(it2 != mapProposals.end())
    {
400
        CBudgetProposal* pbudgetProposal = &((*it2).second);
UdjinM6's avatar
UdjinM6 committed
401
        pbudgetProposal->fValid = pbudgetProposal->IsValid(strError);
402
        ++it2;
403
404
405
    }
}

UdjinM6's avatar
UdjinM6 committed
406
void CBudgetManager::FillBlockPayee(CMutableTransaction& txNew, CAmount nFees)
407
{
408
409
410
411
412
    CBlockIndex* pindexPrev = chainActive.Tip();
    if(!pindexPrev) return;

    int nHighestCount = 0;
    CScript payee;
UdjinM6's avatar
UdjinM6 committed
413
    CAmount nAmount = 0;
414
415

    // ------- Grab The Highest Count
416

417
418
    std::map<uint256, CFinalizedBudget>::iterator it = mapFinalizedBudgets.begin();
    while(it != mapFinalizedBudgets.end())
419
    {
420
        CFinalizedBudget* pfinalizedBudget = &((*it).second);
UdjinM6's avatar
UdjinM6 committed
421
422
423
424
        if(pfinalizedBudget->GetVoteCount() > nHighestCount &&
                pindexPrev->nHeight + 1 >= pfinalizedBudget->GetBlockStart() &&
                pindexPrev->nHeight + 1 <= pfinalizedBudget->GetBlockEnd() &&
                pfinalizedBudget->GetPayeeAndAmount(pindexPrev->nHeight + 1, payee, nAmount)){
425
                    nHighestCount = pfinalizedBudget->GetVoteCount();
426
427
        }

UdjinM6's avatar
UdjinM6 committed
428
        ++it;
429
430
431
432
433
434
    }

    CAmount blockValue = GetBlockValue(pindexPrev->nBits, pindexPrev->nHeight, nFees);

    //miners get the full amount on these blocks
    txNew.vout[0].nValue = blockValue;
435

436
437
438
439
440
441
442
443
444
445
446
    if(nHighestCount > 0){
        txNew.vout.resize(2);

        //these are super blocks, so their value can be much larger than normal
        txNew.vout[1].scriptPubKey = payee;
        txNew.vout[1].nValue = nAmount;

        CTxDestination address1;
        ExtractDestination(payee, address1);
        CBitcoinAddress address2(address1);

UdjinM6's avatar
UdjinM6 committed
447
        LogPrintf("CBudgetManager::FillBlockPayee - Budget payment to %s for %lld\n", address2.ToString(), nAmount);
448
449
450
451
    }

}

452
CFinalizedBudget *CBudgetManager::FindFinalizedBudget(uint256 nHash)
453
{
454
455
    if(mapFinalizedBudgets.count(nHash))
        return &mapFinalizedBudgets[nHash];
Evan Duffield's avatar
Evan Duffield committed
456

457
    return NULL;
458
459
}

460
CBudgetProposal *CBudgetManager::FindProposal(const std::string &strProposalName)
461
{
462
    //find the prop with the highest yes count
463

464
    int nYesCount = -99999;
465
    CBudgetProposal* pbudgetProposal = NULL;
466
467
468
469

    std::map<uint256, CBudgetProposal>::iterator it = mapProposals.begin();
    while(it != mapProposals.end()){
        if((*it).second.strProposalName == strProposalName && (*it).second.GetYeas() > nYesCount){
470
471
            pbudgetProposal = &((*it).second);
            nYesCount = pbudgetProposal->GetYeas();
472
473
474
475
        }
        ++it;
    }

476
    if(nYesCount == -99999) return NULL;
477

478
    return pbudgetProposal;
479
480
481
482
483
484
485
486
487
488
489
}

CBudgetProposal *CBudgetManager::FindProposal(uint256 nHash)
{
    if(mapProposals.count(nHash))
        return &mapProposals[nHash];

    return NULL;
}

bool CBudgetManager::IsBudgetPaymentBlock(int nBlockHeight){
490
491
    int nHighestCount = -1;

492
493
494
    std::map<uint256, CFinalizedBudget>::iterator it = mapFinalizedBudgets.begin();
    while(it != mapFinalizedBudgets.end())
    {
495
        CFinalizedBudget* pfinalizedBudget = &((*it).second);
496
497
498
499
        if(pfinalizedBudget->GetVoteCount() > nHighestCount && 
            nBlockHeight >= pfinalizedBudget->GetBlockStart() && 
            nBlockHeight <= pfinalizedBudget->GetBlockEnd()){
            nHighestCount = pfinalizedBudget->GetVoteCount();
500
501
        }

UdjinM6's avatar
UdjinM6 committed
502
        ++it;
503
504
    }

505
506
507
    /*
        If budget doesn't have 5% of the network votes, then we should pay a masternode instead
    */
UdjinM6's avatar
UdjinM6 committed
508
    if(nHighestCount > mnodeman.CountEnabled(MIN_BUDGET_PEER_PROTO_VERSION)/20) return true;
509

510
511
512
    return false;
}

513
514
515
516
517
bool CBudgetManager::HasNextFinalizedBudget()
{
    CBlockIndex* pindexPrev = chainActive.Tip();
    if(!pindexPrev) return false;

518
519
    if(masternodeSync.IsBudgetFinEmpty()) return true;

520
    int nBlockStart = pindexPrev->nHeight - pindexPrev->nHeight % GetBudgetPaymentCycleBlocks() + GetBudgetPaymentCycleBlocks();
521
    if(nBlockStart - pindexPrev->nHeight > 576*2) return true; //we wouldn't have the budget yet
522
523
524

    if(budget.IsBudgetPaymentBlock(nBlockStart)) return true;

525
526
    LogPrintf("CBudgetManager::HasNextFinalizedBudget() - Client is missing budget\n");

527
528
529
    return false;
}

530
531
532
533
534
535
536
537
538
539
bool CBudgetManager::IsTransactionValid(const CTransaction& txNew, int nBlockHeight)
{
    int nHighestCount = 0;
    std::vector<CFinalizedBudget*> ret;

    // ------- Grab The Highest Count

    std::map<uint256, CFinalizedBudget>::iterator it = mapFinalizedBudgets.begin();
    while(it != mapFinalizedBudgets.end())
    {
540
        CFinalizedBudget* pfinalizedBudget = &((*it).second);
UdjinM6's avatar
UdjinM6 committed
541
542
543
544
        if(pfinalizedBudget->GetVoteCount() > nHighestCount &&
                nBlockHeight >= pfinalizedBudget->GetBlockStart() &&
                nBlockHeight <= pfinalizedBudget->GetBlockEnd()){
                    nHighestCount = pfinalizedBudget->GetVoteCount();
545
546
        }

UdjinM6's avatar
UdjinM6 committed
547
        ++it;
548
549
    }

550
551
552
    /*
        If budget doesn't have 5% of the network votes, then we should pay a masternode instead
    */
UdjinM6's avatar
UdjinM6 committed
553
    if(nHighestCount < mnodeman.CountEnabled(MIN_BUDGET_PEER_PROTO_VERSION)/20) return false;
554
555
556

    // check the highest finalized budgets (+/- 10% to assist in consensus)

UdjinM6's avatar
UdjinM6 committed
557
558
    it = mapFinalizedBudgets.begin();
    while(it != mapFinalizedBudgets.end())
559
    {
UdjinM6's avatar
UdjinM6 committed
560
        CFinalizedBudget* pfinalizedBudget = &((*it).second);
561

UdjinM6's avatar
UdjinM6 committed
562
        if(pfinalizedBudget->GetVoteCount() > nHighestCount - mnodeman.CountEnabled(MIN_BUDGET_PEER_PROTO_VERSION)/10){
563
564
            if(nBlockHeight >= pfinalizedBudget->GetBlockStart() && nBlockHeight <= pfinalizedBudget->GetBlockEnd()){
                if(pfinalizedBudget->IsTransactionValid(txNew, nBlockHeight)){
565
566
567
568
569
                    return true;
                }
            }
        }

UdjinM6's avatar
UdjinM6 committed
570
        ++it;
571
572
573
574
575
576
    }

    //we looked through all of the known budgets
    return false;
}

577
578
std::vector<CBudgetProposal*> CBudgetManager::GetAllProposals()
{
579
    std::vector<CBudgetProposal*> vBudgetProposalRet;
580

UdjinM6's avatar
UdjinM6 committed
581
582
    std::map<uint256, CBudgetProposal>::iterator it = mapProposals.begin();
    while(it != mapProposals.end())
583
    {
584
        (*it).second.CleanAndRemove(false);
585

UdjinM6's avatar
UdjinM6 committed
586
        CBudgetProposal* pbudgetProposal = &((*it).second);
587
        vBudgetProposalRet.push_back(pbudgetProposal);
588

UdjinM6's avatar
UdjinM6 committed
589
        ++it;
590
591
    }

592
    return vBudgetProposalRet;
593
594
}

Evan Duffield's avatar
Evan Duffield committed
595
596
597
//
// Sort by votes, if there's a tie sort by their feeHash TX
//
UdjinM6's avatar
UdjinM6 committed
598
599
struct sortProposalsByVotes {
    bool operator()(const std::pair<CBudgetProposal*, int> &left, const std::pair<CBudgetProposal*, int> &right) {
Evan Duffield's avatar
Evan Duffield committed
600
601
602
      if( left.second != right.second)
        return (left.second > right.second);
      return (left.first->nFeeTXHash > right.first->nFeeTXHash);
UdjinM6's avatar
UdjinM6 committed
603
604
605
    }
};

606
607
608
609
610
//Need to review this function
std::vector<CBudgetProposal*> CBudgetManager::GetBudget()
{
    // ------- Sort budgets by Yes Count

UdjinM6's avatar
UdjinM6 committed
611
    std::vector<std::pair<CBudgetProposal*, int> > vBudgetPorposalsSort;
612
613
614

    std::map<uint256, CBudgetProposal>::iterator it = mapProposals.begin();
    while(it != mapProposals.end()){
615
        (*it).second.CleanAndRemove(false);
Evan Duffield's avatar
Evan Duffield committed
616
        vBudgetPorposalsSort.push_back(make_pair(&((*it).second), (*it).second.GetYeas()-(*it).second.GetNays()));
617
618
619
        ++it;
    }

UdjinM6's avatar
UdjinM6 committed
620
    std::sort(vBudgetPorposalsSort.begin(), vBudgetPorposalsSort.end(), sortProposalsByVotes());
621
622
623

    // ------- Grab The Budgets In Order

UdjinM6's avatar
UdjinM6 committed
624
    std::vector<CBudgetProposal*> vBudgetProposalsRet;
625

UdjinM6's avatar
UdjinM6 committed
626
    CAmount nBudgetAllocated = 0;
627
    CBlockIndex* pindexPrev = chainActive.Tip();
UdjinM6's avatar
UdjinM6 committed
628
    if(pindexPrev == NULL) return vBudgetProposalsRet;
629

UdjinM6's avatar
UdjinM6 committed
630
631
632
    int nBlockStart = pindexPrev->nHeight - pindexPrev->nHeight % GetBudgetPaymentCycleBlocks() + GetBudgetPaymentCycleBlocks();
    int nBlockEnd  =  nBlockStart + GetBudgetPaymentCycleBlocks() - 1;
    CAmount nTotalBudget = GetTotalBudget(nBlockStart);
633
634


UdjinM6's avatar
UdjinM6 committed
635
636
    std::vector<std::pair<CBudgetProposal*, int> >::iterator it2 = vBudgetPorposalsSort.begin();
    while(it2 != vBudgetPorposalsSort.end())
637
    {
UdjinM6's avatar
UdjinM6 committed
638
        CBudgetProposal* pbudgetProposal = (*it2).first;
639

Evan Duffield's avatar
Evan Duffield committed
640
        //prop start/end should be inside this period
UdjinM6's avatar
UdjinM6 committed
641
642
        if(pbudgetProposal->fValid && pbudgetProposal->nBlockStart <= nBlockStart &&
                pbudgetProposal->nBlockEnd >= nBlockEnd &&
Evan Duffield's avatar
Evan Duffield committed
643
644
                pbudgetProposal->GetYeas() - pbudgetProposal->GetNays() > mnodeman.CountEnabled(MIN_BUDGET_PEER_PROTO_VERSION)/10 && 
                pbudgetProposal->IsEstablished())
Evan Duffield's avatar
Evan Duffield committed
645
        {
Evan Duffield's avatar
Evan Duffield committed
646
            if(pbudgetProposal->GetAmount() + nBudgetAllocated <= nTotalBudget) {
647
648
                pbudgetProposal->SetAllotted(pbudgetProposal->GetAmount());
                nBudgetAllocated += pbudgetProposal->GetAmount();
Evan Duffield's avatar
Evan Duffield committed
649
            } else {
Evan Duffield's avatar
Evan Duffield committed
650
                pbudgetProposal->SetAllotted(0);
Evan Duffield's avatar
Evan Duffield committed
651
            }
Evan Duffield's avatar
Evan Duffield committed
652
            
UdjinM6's avatar
UdjinM6 committed
653
            vBudgetProposalsRet.push_back(pbudgetProposal);
654
655
        }

UdjinM6's avatar
UdjinM6 committed
656
        ++it2;
657
658
    }

UdjinM6's avatar
UdjinM6 committed
659
    return vBudgetProposalsRet;
660
661
}

UdjinM6's avatar
UdjinM6 committed
662
663
664
665
666
667
struct sortFinalizedBudgetsByVotes {
    bool operator()(const std::pair<CFinalizedBudget*, int> &left, const std::pair<CFinalizedBudget*, int> &right) {
        return left.second > right.second;
    }
};

668
669
std::vector<CFinalizedBudget*> CBudgetManager::GetFinalizedBudgets()
{
UdjinM6's avatar
UdjinM6 committed
670
671
    std::vector<CFinalizedBudget*> vFinalizedBudgetsRet;
    std::vector<std::pair<CFinalizedBudget*, int> > vFinalizedBudgetsSort;
672
673
674

    // ------- Grab The Budgets In Order

UdjinM6's avatar
UdjinM6 committed
675
676
    std::map<uint256, CFinalizedBudget>::iterator it = mapFinalizedBudgets.begin();
    while(it != mapFinalizedBudgets.end())
677
    {
UdjinM6's avatar
UdjinM6 committed
678
679
680
681
682
683
        CFinalizedBudget* pfinalizedBudget = &((*it).second);

        vFinalizedBudgetsSort.push_back(make_pair(pfinalizedBudget, pfinalizedBudget->GetVoteCount()));
        ++it;
    }
    std::sort(vFinalizedBudgetsSort.begin(), vFinalizedBudgetsSort.end(), sortFinalizedBudgetsByVotes());
684

UdjinM6's avatar
UdjinM6 committed
685
686
687
688
689
    std::vector<std::pair<CFinalizedBudget*, int> >::iterator it2 = vFinalizedBudgetsSort.begin();
    while(it2 != vFinalizedBudgetsSort.end())
    {
        vFinalizedBudgetsRet.push_back((*it2).first);
        ++it2;
690
691
    }

UdjinM6's avatar
UdjinM6 committed
692
    return vFinalizedBudgetsRet;
693
694
}

UdjinM6's avatar
UdjinM6 committed
695
std::string CBudgetManager::GetRequiredPaymentsString(int nBlockHeight)
696
697
698
699
700
701
{
    std::string ret = "unknown-budget";

    std::map<uint256, CFinalizedBudget>::iterator it = mapFinalizedBudgets.begin();
    while(it != mapFinalizedBudgets.end())
    {
702
703
        CFinalizedBudget* pfinalizedBudget = &((*it).second);
        if(nBlockHeight >= pfinalizedBudget->GetBlockStart() && nBlockHeight <= pfinalizedBudget->GetBlockEnd()){
704
            CTxBudgetPayment payment;
UdjinM6's avatar
UdjinM6 committed
705
            if(pfinalizedBudget->GetBudgetPaymentByBlock(nBlockHeight, payment)){
706
                if(ret == "unknown-budget"){
UdjinM6's avatar
UdjinM6 committed
707
                    ret = payment.nProposalHash.ToString();
708
709
                } else {
                    ret += ",";
UdjinM6's avatar
UdjinM6 committed
710
                    ret += payment.nProposalHash.ToString();
711
712
                }
            } else {
UdjinM6's avatar
UdjinM6 committed
713
                LogPrintf("CBudgetManager::GetRequiredPaymentsString - Couldn't find budget payment for block %d\n", nBlockHeight);
714
715
716
            }
        }

UdjinM6's avatar
UdjinM6 committed
717
        ++it;
718
719
720
721
722
    }

    return ret;
}

UdjinM6's avatar
UdjinM6 committed
723
CAmount CBudgetManager::GetTotalBudget(int nHeight)
724
725
726
{
    if(chainActive.Tip() == NULL) return 0;

727
    //get min block value and calculate from that
UdjinM6's avatar
UdjinM6 committed
728
    CAmount nSubsidy = 5 * COIN;
729
730
731
732
733
734
735
736

    if(Params().NetworkID() == CBaseChainParams::TESTNET){
        for(int i = 46200; i <= nHeight; i += 210240) nSubsidy -= nSubsidy/14;
    } else {
        // yearly decline of production by 7.1% per year, projected 21.3M coins max by year 2050.
        for(int i = 210240; i <= nHeight; i += 210240) nSubsidy -= nSubsidy/14;
    }

Evan Duffield's avatar
Evan Duffield committed
737
738
739
740
741
    // Amount of blocks in a months period of time (using 2.6 minutes per) = (60*24*30)/2.6
    if(Params().NetworkID() == CBaseChainParams::MAIN) return ((nSubsidy/100)*10)*576*30;

    //for testing purposes
    return ((nSubsidy/100)*10)*50;
742
743
744
745
}

void CBudgetManager::NewBlock()
{
Evan Duffield's avatar
Evan Duffield committed
746
    if (masternodeSync.RequestedMasternodeAssets <= MASTERNODE_SYNC_BUDGET) return;
747
748
749
750
751
752
753

    if (strBudgetMode == "suggest") { //suggest the budget we see
        SubmitFinalBudget();
    }

    //this function should be called 1/6 blocks, allowing up to 100 votes per day on all proposals
    if(chainActive.Height() % 6 != 0) return;
754

755
    CheckAndRemove();
756
757
758

    //remove invalid votes once in a while (we have to check the signatures and validity of every vote, somewhat CPU intensive)

759
760
761
762
763
764
765
    std::map<uint256, int64_t>::iterator it = askedForSourceProposalOrBudget.begin();
    while(it != askedForSourceProposalOrBudget.end()){
        if((*it).second > GetTime() - (60*60*24)){
            ++it;
        } else {
            askedForSourceProposalOrBudget.erase(it++);
        }
766
767
    }

768
769
    std::map<uint256, CBudgetProposal>::iterator it2 = mapProposals.begin();
    while(it2 != mapProposals.end()){
770
        (*it2).second.CleanAndRemove(false);
771
772
        ++it2;
    }
773
774
775

    std::map<uint256, CFinalizedBudget>::iterator it3 = mapFinalizedBudgets.begin();
    while(it3 != mapFinalizedBudgets.end()){
776
        (*it3).second.CleanAndRemove(false);
777
778
        ++it3;
    }
779
780
781
782
783
784
785
786
}

void CBudgetManager::ProcessMessage(CNode* pfrom, std::string& strCommand, CDataStream& vRecv)
{
    // lite mode is not supported
    if(IsInitialBlockDownload()) return;

    LOCK(cs_budget);
787
788

    if (strCommand == "mnvs") { //Masternode vote sync
Evan Duffield's avatar
Evan Duffield committed
789
790
791
        uint256 nProp;
        vRecv >> nProp;

792
793
794
795
796
797
798
        if(Params().NetworkID() == CBaseChainParams::MAIN){
            if(pfrom->HasFulfilledRequest("mnvs")) {
                LogPrintf("mnvs - peer already asked me for the list\n");
                Misbehaving(pfrom->GetId(), 20);
                return;
            }
            pfrom->FulfilledRequest("mnvs");
799
800
        }

UdjinM6's avatar
UdjinM6 committed
801
        Sync(pfrom, nProp);
802
        LogPrintf("mnvs - Sent Masternode votes to %s\n", pfrom->addr.ToString());
803
804
    }

805
    if (strCommand == "mprop") { //Masternode Proposal
806
807
        CBudgetProposalBroadcast budgetProposalBroadcast;
        vRecv >> budgetProposalBroadcast;
808

809
        if(mapSeenMasternodeBudgetProposals.count(budgetProposalBroadcast.GetHash())){
810
            masternodeSync.AddedBudgetItem();
Evan Duffield's avatar
Evan Duffield committed
811
            return;
812
813
        }

Evan Duffield's avatar
Evan Duffield committed
814
815
        mapSeenMasternodeBudgetProposals.insert(make_pair(budgetProposalBroadcast.GetHash(), budgetProposalBroadcast));

Evan Duffield's avatar
Evan Duffield committed
816
        std::string strError = "";
817
        if(!budgetProposalBroadcast.IsValid(strError)) {
Evan Duffield's avatar
Evan Duffield committed
818
            LogPrintf("mprop - invalid budget proposal - %s\n", strError);
819
            return;
820
821
        }

Evan Duffield's avatar
Evan Duffield committed
822
        CBudgetProposal budgetProposal(budgetProposalBroadcast);
Evan Duffield's avatar
Evan Duffield committed
823
        if(AddProposal(budgetProposal)) {budgetProposalBroadcast.Relay();}
824
        masternodeSync.AddedBudgetItem();
825

826
827
        LogPrintf("mprop - new budget - %s\n", budgetProposalBroadcast.GetHash().ToString());

Evan Duffield's avatar
Evan Duffield committed
828
829
        //We might have active votes for this proposal that are valid now
        CheckOrphanVotes();
830
831
    }

832
833
834
    if (strCommand == "mvote") { //Masternode Vote
        CBudgetVote vote;
        vRecv >> vote;
835
        vote.fValid = true;
836

Evan Duffield's avatar
Evan Duffield committed
837
        if(mapSeenMasternodeBudgetVotes.count(vote.GetHash())){
838
            masternodeSync.AddedBudgetItem();
839
840
841
            return;
        }

842
843
844
        CMasternode* pmn = mnodeman.Find(vote.vin);
        if(pmn == NULL) {
            if(fDebug) LogPrintf("mvote - unknown masternode - vin: %s\n", vote.vin.ToString());
845
            return;
846
847
        }

848

849
        mapSeenMasternodeBudgetVotes.insert(make_pair(vote.GetHash(), vote));
850
        if(!vote.SignatureValid(true)){
851
852
            LogPrintf("mvote - signature invalid\n");
            Misbehaving(pfrom->GetId(), 20);
853
            return;
854
        }
855
856
857
858
        
        std::string strError = "";
        if(UpdateProposal(vote, pfrom, strError)) {
            vote.Relay();
859
            masternodeSync.AddedBudgetItem();
860
        }
861
862

        LogPrintf("mvote - new budget vote - %s\n", vote.GetHash().ToString());
863
864
    }

865
    if (strCommand == "fbs") { //Finalized Budget Suggestion
866
867
        CFinalizedBudgetBroadcast finalizedBudgetBroadcast;
        vRecv >> finalizedBudgetBroadcast;
868

869
        if(mapSeenFinalizedBudgets.count(finalizedBudgetBroadcast.GetHash())){
870
            masternodeSync.AddedBudgetItem();
871
872
873
            return;
        }

Evan Duffield's avatar
Evan Duffield committed
874
        std::string strError = "";
875
        if(!IsBudgetCollateralValid(finalizedBudgetBroadcast.nFeeTXHash, finalizedBudgetBroadcast.GetHash(), strError)){
Evan Duffield's avatar
Evan Duffield committed
876
            LogPrintf("Finalized Budget FeeTX is not valid - %s - %s\n", finalizedBudgetBroadcast.nFeeTXHash.ToString(), strError);
877
878
879
            return;
        }

Evan Duffield's avatar
Evan Duffield committed
880
881
        mapSeenFinalizedBudgets.insert(make_pair(finalizedBudgetBroadcast.GetHash(), finalizedBudgetBroadcast));

Evan Duffield's avatar
Evan Duffield committed
882
883
        if(!finalizedBudgetBroadcast.IsValid(strError)) {
            LogPrintf("fbs - invalid finalized budget - %s\n", strError);
884
            return;
885
886
        }

887
888
        LogPrintf("fbs - new finalized budget - %s\n", finalizedBudgetBroadcast.GetHash().ToString());

Evan Duffield's avatar
Evan Duffield committed
889
        CFinalizedBudget finalizedBudget(finalizedBudgetBroadcast);
Evan Duffield's avatar
Evan Duffield committed
890
        if(AddFinalizedBudget(finalizedBudget)) {finalizedBudgetBroadcast.Relay();}
891
        masternodeSync.AddedBudgetItem();
892

Evan Duffield's avatar
Evan Duffield committed
893
894
        //we might have active votes for this budget that are now valid
        CheckOrphanVotes();
895
896
897
898
899
    }

    if (strCommand == "fbvote") { //Finalized Budget Vote
        CFinalizedBudgetVote vote;
        vRecv >> vote;
900
        vote.fValid = true;
901

Evan Duffield's avatar
Evan Duffield committed
902
        if(mapSeenFinalizedBudgetVotes.count(vote.GetHash())){
903
            masternodeSync.AddedBudgetItem();
904
905
906
            return;
        }

907
908
909
        CMasternode* pmn = mnodeman.Find(vote.vin);
        if(pmn == NULL) {
            if(fDebug) LogPrintf("fbvote - unknown masternode - vin: %s\n", vote.vin.ToString());
910
911
912
            return;
        }

913
        mapSeenFinalizedBudgetVotes.insert(make_pair(vote.GetHash(), vote));
914
        if(!vote.SignatureValid(true)){
915
916
            LogPrintf("fbvote - signature invalid\n");
            Misbehaving(pfrom->GetId(), 20);
917
            return;
918
919
        }

920
921
922
        std::string strError = "";
        if(UpdateFinalizedBudget(vote, pfrom, strError)) {
            vote.Relay();
923
            masternodeSync.AddedBudgetItem();
924
        }
925
926

        if(fDebug) LogPrintf("fbs - new finalized budget vote - %s\n", vote.GetHash().ToString());
927
    }
928
929
}

930
bool CBudgetManager::PropExists(uint256 nHash)
931
{
932
933
    if(mapProposals.count(nHash)) return true;
    return false;
934
935
}

936
void CBudgetManager::Sync(CNode* pfrom, uint256 nProp)
937
{
938
    /*
939
940
941
942
        Sync with a client on the network

        --

943
944
        This code checks each of the hash maps for all known budget proposals and finalized budget proposals, then checks them against the
        budget object to see if they're OK. If all checks pass, we'll send it to the peer.
945
946
947

    */

948
949
    vector<CInv> vInv;

950
951
    std::map<uint256, CBudgetProposalBroadcast>::iterator it1 = mapSeenMasternodeBudgetProposals.begin();
    while(it1 != mapSeenMasternodeBudgetProposals.end()){
UdjinM6's avatar
UdjinM6 committed
952
        CBudgetProposal* pbudgetProposal = FindProposal((*it1).first);
Evan Duffield's avatar
Evan Duffield committed
953
        if(pbudgetProposal && pbudgetProposal->fValid && (nProp == 0 || (*it1).first == nProp)){
Evan Duffield's avatar
Evan Duffield committed
954
            CInv inv(MSG_BUDGET_PROPOSAL, (*it1).second.GetHash());
955
            vInv.push_back(inv);
956
957
958
959
960
        
            //send votes
            std::map<uint256, CBudgetVote>::iterator it2 = pbudgetProposal->mapVotes.begin();
            while(it2 != pbudgetProposal->mapVotes.end()){
                if((*it2).second.fValid){
Evan Duffield's avatar
Evan Duffield committed
961
                    CInv inv(MSG_BUDGET_VOTE, (*it2).second.GetHash());
962
                    vInv.push_back(inv);
963
                }
UdjinM6's avatar
UdjinM6 committed
964
                ++it2;
965
            }
966
        }
UdjinM6's avatar
UdjinM6 committed
967
        ++it1;
968
969
    }

Evan Duffield's avatar
Evan Duffield committed
970
971
972
973
    pfrom->PushMessage("ssc", MASTERNODE_SYNC_BUDGET_PROP, (int)vInv.size());
    if(vInv.size() > 0) pfrom->PushMessage("inv", vInv);
    vInv.clear();

974
975
    std::map<uint256, CFinalizedBudgetBroadcast>::iterator it3 = mapSeenFinalizedBudgets.begin();
    while(it3 != mapSeenFinalizedBudgets.end()){
UdjinM6's avatar
UdjinM6 committed
976
        CFinalizedBudget* pfinalizedBudget = FindFinalizedBudget((*it3).first);
Evan Duffield's avatar
Evan Duffield committed
977
        if(pfinalizedBudget && pfinalizedBudget->fValid && (nProp == 0 || (*it3).first == nProp)){
Evan Duffield's avatar
Evan Duffield committed
978
            CInv inv(MSG_BUDGET_FINALIZED, (*it3).second.GetHash());
979
            vInv.push_back(inv);
980
981
982
983

            //send votes
            std::map<uint256, CFinalizedBudgetVote>::iterator it4 = pfinalizedBudget->mapVotes.begin();
            while(it4 != pfinalizedBudget->mapVotes.end()){
984
                if((*it4).second.fValid) {
Evan Duffield's avatar
Evan Duffield committed
985
                    CInv inv(MSG_BUDGET_FINALIZED_VOTE, (*it4).second.GetHash());
986
                    vInv.push_back(inv);
987
                }
UdjinM6's avatar
UdjinM6 committed
988
                ++it4;
989
            }
990
        }
UdjinM6's avatar
UdjinM6 committed
991
        ++it3;
992
    }
993

Evan Duffield's avatar
Evan Duffield committed
994
    pfrom->PushMessage("ssc", MASTERNODE_SYNC_BUDGET_FIN, (int)vInv.size());
995
    if(vInv.size() > 0) pfrom->PushMessage("inv", vInv);
996
}
997

998
bool CBudgetManager::UpdateProposal(CBudgetVote& vote, CNode* pfrom, std::string& strError)
999
1000
{
    LOCK(cs);
1001

1002
    if(!mapProposals.count(vote.nProposalHash)){
1003
        if(pfrom){
1004
1005
1006
1007
            // only ask for missing items after our syncing process is complete -- 
            //   otherwise we'll think a full sync succeeded when they return a result
            if(!masternodeSync.IsSynced()) return false;

UdjinM6's avatar
UdjinM6 committed
1008
            LogPrintf("CBudgetManager::UpdateProposal - Unknown proposal %d, asking for source proposal\n", vote.nProposalHash.ToString());
1009
1010
1011
1012
1013
1014
1015
1016
            mapOrphanMasternodeBudgetVotes[vote.nProposalHash] = vote;

            if(!askedForSourceProposalOrBudget.count(vote.nProposalHash)){
                pfrom->PushMessage("mnvs", vote.nProposalHash);
                askedForSourceProposalOrBudget[vote.nProposalHash] = GetTime();
            }
        }

1017
        strError = "Proposal not found!";
1018
        return false;
1019
    }
1020

1021

1022
    return mapProposals[vote.nProposalHash].AddOrUpdateVote(vote, strError);
1023
1024
}

1025
bool CBudgetManager::UpdateFinalizedBudget(CFinalizedBudgetVote& vote, CNode* pfrom, std::string& strError)
1026
{
1027
    LOCK(cs);
1028

1029
    if(!mapFinalizedBudgets.count(vote.nBudgetHash)){
1030
        if(pfrom){
1031
1032
1033
1034
            // only ask for missing items after our syncing process is complete -- 
            //   otherwise we'll think a full sync succeeded when they return a result
            if(!masternodeSync.IsSynced()) return false;

UdjinM6's avatar
UdjinM6 committed
1035
            LogPrintf("CBudgetManager::UpdateFinalizedBudget - Unknown Finalized Proposal %s, asking for source budget\n", vote.nBudgetHash.ToString());
1036
1037
1038
1039
1040
1041
1042
1043
            mapOrphanFinalizedBudgetVotes[vote.nBudgetHash] = vote;

            if(!askedForSourceProposalOrBudget.count(vote.nBudgetHash)){
                pfrom->PushMessage("mnvs", vote.nBudgetHash);
                askedForSourceProposalOrBudget[vote.nBudgetHash] = GetTime();
            }

        }
1044
1045

        strError = "Finalized Budget not found!";
1046
        return false;
1047
1048
    }

1049
    return mapFinalizedBudgets[vote.nBudgetHash].AddOrUpdateVote(vote, strError);
1050
1051
}

1052
CBudgetProposal::CBudgetProposal()
1053
{
1054
1055
1056
1057
    strProposalName = "unknown";
    nBlockStart = 0;
    nBlockEnd = 0;
    nAmount = 0;
Evan Duffield's avatar
Evan Duffield committed
1058
    nTime = 0;
1059
    fValid = true;
1060
1061
}

1062
CBudgetProposal::CBudgetProposal(std::string strProposalNameIn, std::string strURLIn, int nBlockStartIn, int nBlockEndIn, CScript addressIn, CAmount nAmountIn, uint256 nFeeTXHashIn, int64_t nTimeIn)
1063
1064
1065
{
    strProposalName = strProposalNameIn;
    strURL = strURLIn;
1066
1067
1068
1069
    nBlockStart = nBlockStartIn;
    nBlockEnd = nBlockEndIn;
    address = addressIn;
    nAmount = nAmountIn;
1070
    nTime = nTimeIn;
Evan Duffield's avatar
Evan Duffield committed
1071
    nFeeTXHash = nFeeTXHashIn;
1072
    fValid = true;
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
}

CBudgetProposal::CBudgetProposal(const CBudgetProposal& other)
{
    strProposalName = other.strProposalName;
    strURL = other.strURL;
    nBlockStart = other.nBlockStart;
    nBlockEnd = other.nBlockEnd;
    address = other.address;
    nAmount = other.nAmount;
Evan Duffield's avatar
Evan Duffield committed
1083
    nTime = other.nTime;
Evan Duffield's avatar
Evan Duffield committed
1084
1085
    nFeeTXHash = other.nFeeTXHash;
    mapVotes = other.mapVotes;
1086
    fValid = true;
1087
1088
}

1089
bool CBudgetProposal::IsValid(std::string& strError, bool fCheckCollateral)
1090
{
UdjinM6's avatar
UdjinM6 committed
1091
    if(GetNays() - GetYeas() > mnodeman.CountEnabled(MIN_BUDGET_PEER_PROTO_VERSION)/10){
1092
1093
1094
1095
         strError = "Active removal";
         return false;
    }

1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
    if(nBlockStart < 0) {
        strError = "Invalid Proposal";
        return false;
    }

    if(address == CScript()) {
        strError = "Invalid Payment Address";
        return false;
    }

1106
1107
1108
1109
    if(fCheckCollateral){
        if(!IsBudgetCollateralValid(nFeeTXHash, GetHash(), strError)){
            return false;
        }
Evan Duffield's avatar
Evan Duffield committed
1110
1111
    }

Evan Duffield's avatar
Evan Duffield committed
1112
    if(masternodeSync.IsSynced()) {
1113
        if(nTime < GetTime() - (60*60*2) || nTime < GetTime() - (60*60)) {
Evan Duffield's avatar
Evan Duffield committed
1114
1115
1116
1117
1118
            strError = "Time is out of acceptable range.";
            return false;
        }
    }

1119
1120
1121
1122
    /*
        TODO: There might be an issue with multisig in the coinbase on mainnet, we will add support for it in a future release.
    */
    if(address.IsPayToScriptHash()) {
1123
        strError = "Multisig is not currently supported.";
1124
1125
1126
        return false;
    }

Evan Duffield's avatar
Evan Duffield committed
1127
1128
    //if proposal doesn't gain traction within 2 weeks, remove it
    // nTime not being saved correctly
1129
    // -- TODO: We should keep track of the last time the proposal was valid, if it's invalid for 2 weeks, erase it
Evan Duffield's avatar
Evan Duffield committed
1130
    // if(nTime + (60*60*24*2) < GetAdjustedTime()) {
UdjinM6's avatar
UdjinM6 committed
1131
    //     if(GetYeas()-GetNays() < (mnodeman.CountEnabled(MIN_BUDGET_PEER_PROTO_VERSION)/10)) {
Evan Duffield's avatar
Evan Duffield committed
1132
1133
1134
1135
    //         strError = "Not enough support";
    //         return false;
    //     }
    // }
1136

1137
1138
    //can only pay out 10% of the possible coins (min value of coins)
    if(nAmount > budget.GetTotalBudget(nBlockStart)) {
Evan Duffield's avatar
Evan Duffield committed
1139
        strError = "Payment more than max";
1140
1141
1142
        return false;
    }

1143
1144
1145
1146
1147
1148
    CBlockIndex* pindexPrev = chainActive.Tip();
    if(pindexPrev == NULL) {strError = "Tip is NULL"; return true;}

    if(GetBlockEnd() < pindexPrev->nHeight - GetBudgetPaymentCycleBlocks()/2 ) return false;


1149
1150
1151
    return true;
}

1152
bool CBudgetProposal::AddOrUpdateVote(CBudgetVote& vote, std::string& strError)
1153
1154
1155
1156
{
    LOCK(cs);

    uint256 hash = vote.vin.prevout.GetHash();
Evan Duffield's avatar
Evan Duffield committed
1157
1158
1159

    if(mapVotes.count(hash)){
        if(mapVotes[hash].nTime > vote.nTime){
1160
1161
            strError = strprintf("new vote older than existing vote - %s\n", vote.GetHash().ToString());
            if(fDebug) LogPrintf("CBudgetProposal::AddOrUpdateVote - %s\n", strError);
Evan Duffield's avatar
Evan Duffield committed
1162
            return false;
Evan Duffield's avatar
Evan Duffield committed
1163
        }
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
        if(vote.nTime - mapVotes[hash].nTime < BUDGET_VOTE_UPDATE_MIN){
            strError = strprintf("time between votes is too soon - %s - %lli\n", vote.GetHash().ToString(), vote.nTime - mapVotes[hash].nTime);
            if(fDebug) LogPrintf("CBudgetProposal::AddOrUpdateVote - %s\n", strError);
            return false;
        }
    }

    //if we're synced, the vote should have been recent
    if(masternodeSync.IsSynced()) {
        //up to an hour ago
        if(vote.nTime < GetTime() - (60*60)){
            strError = strprintf("new vote is too old - %s - nTime %lli - Min Time %lli\n", vote.GetHash().ToString(), vote.nTime, GetTime() - (60*60));
            if(fDebug) LogPrintf("CBudgetProposal::AddOrUpdateVote - %s\n", strError);
            return false;
        }        
Evan Duffield's avatar
Evan Duffield committed
1179
1180
    }

1181
1182
1183
1184
1185
1186
    if(vote.nTime > GetTime() + (60*60)){
        strError = strprintf("new vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", vote.GetHash().ToString(), vote.nTime, GetTime() + (60*60));
        if(fDebug) LogPrintf("CBudgetProposal::AddOrUpdateVote - %s\n", strError);
        return false;
    }        

1187
    mapVotes[hash] = vote;
Evan Duffield's avatar
Evan Duffield committed
1188
    return true;
1189
1190
}

1191
// If masternode voted for a proposal, but is now invalid -- remove the vote
1192
void CBudgetProposal::CleanAndRemove(bool fSignatureCheck)
1193
1194
1195
1196
{
    std::map<uint256, CBudgetVote>::iterator it = mapVotes.begin();

    while(it != mapVotes.end()) {
1197
        (*it).second.fValid = (*it).second.SignatureValid(fSignatureCheck);
1198
        ++it;
1199
1200
1201
    }
}

1202
1203
1204
1205
double CBudgetProposal::GetRatio()
{
    int yeas = 0;
    int nays = 0;
1206

1207
    std::map<uint256, CBudgetVote>::iterator it = mapVotes.begin();
1208

1209
1210
1211
1212
1213
    while(it != mapVotes.end()) {
        if ((*it).second.nVote == VOTE_YES) yeas++;
        if ((*it).second.nVote == VOTE_NO) nays++;
        ++it;
    }
1214

1215
    if(yeas+nays == 0) return 0.0f;
1216

1217
    return ((double)(yeas) / (double)(yeas+nays));
1218
1219
}

1220
int CBudgetProposal::GetYeas()
1221
{
1222
    int ret = 0;
1223
1224

    std::map<uint256, CBudgetVote>::iterator it = mapVotes.begin();
1225
    while(it != mapVotes.end()){
1226
        if ((*it).second.nVote == VOTE_YES && (*it).second.fValid) ret++;
1227
        ++it;
1228
1229
    }

1230
    return ret;
1231
1232
}

1233
int CBudgetProposal::GetNays()
1234
{
1235
    int ret = 0;
1236

1237
1238
    std::map<uint256, CBudgetVote>::iterator it = mapVotes.begin();
    while(it != mapVotes.end()){
1239
        if ((*it).second.nVote == VOTE_NO && (*it).second.fValid) ret++;
1240
        ++it;
1241
    }
1242

1243
    return ret;
1244
1245
}

1246
int CBudgetProposal::GetAbstains()
1247
{
1248
    int ret = 0;
1249

1250
    std::map<uint256, CBudgetVote>::iterator it = mapVotes.begin();
1251
    while(it != mapVotes.end()){
1252
        if ((*it).second.nVote == VOTE_ABSTAIN && (*it).second.fValid) ret++;
1253
        ++it;
1254
    }
1255

1256
    return ret;
1257
1258
}

1259
int CBudgetProposal::GetBlockStartCycle()
1260
{
1261
    //end block is half way through the next cycle (so the proposal will be removed much after the payment is sent)
1262

UdjinM6's avatar
UdjinM6 committed
1263
    return nBlockStart - nBlockStart % GetBudgetPaymentCycleBlocks();
1264
1265
}

1266
int CBudgetProposal::GetBlockCurrentCycle()
1267
{
1268
1269
    CBlockIndex* pindexPrev = chainActive.Tip();
    if(pindexPrev == NULL) return -1;
1270

1271
    if(pindexPrev->nHeight >= GetBlockEndCycle()) return -1;
1272

UdjinM6's avatar
UdjinM6 committed
1273
    return pindexPrev->nHeight - pindexPrev->nHeight % GetBudgetPaymentCycleBlocks();
1274
1275
}

1276
int CBudgetProposal::GetBlockEndCycle()
1277
{
1278
    //end block is half way through the next cycle (so the proposal will be removed much after the payment is sent)
1279

UdjinM6's avatar
UdjinM6 committed
1280
    return nBlockEnd - GetBudgetPaymentCycleBlocks()/2;
1281
1282
}

1283
int CBudgetProposal::GetTotalPaymentCount()
1284
{
UdjinM6's avatar
UdjinM6 committed
1285
    return (GetBlockEndCycle() - GetBlockStartCycle()) / GetBudgetPaymentCycleBlocks();
1286
1287
}

1288
int CBudgetProposal::GetRemainingPaymentCount()
1289
{
1290
1291
1292
1293
1294
    // If this budget starts in the future, this value will be wrong
    int nPayments = (GetBlockEndCycle() - GetBlockCurrentCycle()) / GetBudgetPaymentCycleBlocks();
    int nTotal = (GetBlockEndCycle() - GetBlockStartCycle()) / GetBudgetPaymentCycleBlocks();
    // Take the lowest value
    return (nPayments <= nTotal ? nPayments : nTotal);
1295
1296
}

1297
1298
1299
CBudgetProposalBroadcast::CBudgetProposalBroadcast()
{
    strProposalName = "unknown";
1300
    strURL = "";
1301
1302
1303
    nBlockStart = 0;
    nBlockEnd = 0;
    nAmount = 0;
Evan Duffield's avatar
Evan Duffield committed
1304
    nTime = 0;
Evan Duffield's avatar
Evan Duffield committed
1305
    nFeeTXHash = 0;
1306
1307
1308
1309
1310
}

CBudgetProposalBroadcast::CBudgetProposalBroadcast(const CBudgetProposal& other)
{
    strProposalName = other.strProposalName;
1311
    strURL = other.strURL;
1312
1313
1314
1315
    nBlockStart = other.nBlockStart;
    nBlockEnd = other.nBlockEnd;
    address = other.address;
    nAmount = other.nAmount;
Evan Duffield's avatar
Evan Duffield committed
1316
    nFeeTXHash = other.nFeeTXHash;
1317
    nTime = other.nTime;
1318
1319
}

1320
CBudgetProposalBroadcast::CBudgetProposalBroadcast(std::string strProposalNameIn, std::string strURLIn, int nPaymentCount, CScript addressIn, CAmount nAmountIn, int nBlockStartIn, uint256 nFeeTXHashIn, int64_t nTimeIn)
1321
1322
1323
1324
1325
1326
{
    strProposalName = strProposalNameIn;
    strURL = strURLIn;

    nBlockStart = nBlockStartIn;

UdjinM6's avatar
UdjinM6 committed
1327
    int nCycleStart = nBlockStart - nBlockStart % GetBudgetPaymentCycleBlocks();
1328
    //calculate the end of the cycle for this vote, add half a cycle (vote will be deleted after that block)
UdjinM6's avatar
UdjinM6 committed
1329
    nBlockEnd = nCycleStart + GetBudgetPaymentCycleBlocks() * nPaymentCount + GetBudgetPaymentCycleBlocks()/2;
1330
1331
1332

    address = addressIn;
    nAmount = nAmountIn;
1333

Evan Duffield's avatar
Evan Duffield committed
1334
    nFeeTXHash = nFeeTXHashIn;
1335
1336

    nTime = nTimeIn;
1337
1338
1339
1340
1341
}

void CBudgetProposalBroadcast::Relay()
{
    CInv inv(MSG_BUDGET_PROPOSAL, GetHash());
UdjinM6's avatar
UdjinM6 committed
1342
    RelayInv(inv, MIN_BUDGET_PEER_PROTO_VERSION);
1343
1344
}

1345
CBudgetVote::CBudgetVote()
1346
{
1347
1348
1349
1350
    vin = CTxIn();
    nProposalHash = 0;
    nVote = VOTE_ABSTAIN;
    nTime = 0;
1351
    fValid = true;
1352
1353
}

1354
CBudgetVote::CBudgetVote(CTxIn vinIn, uint256 nProposalHashIn, int nVoteIn)
1355
{
1356
1357
1358
1359
    vin = vinIn;
    nProposalHash = nProposalHashIn;
    nVote = nVoteIn;
    nTime = GetAdjustedTime();
1360
    fValid = true;
1361
1362
}

1363
void CBudgetVote::Relay()
1364
{
1365
    CInv inv(MSG_BUDGET_VOTE, GetHash());
UdjinM6's avatar
UdjinM6 committed
1366
    RelayInv(inv, MIN_BUDGET_PEER_PROTO_VERSION);
1367
1368
}

1369
bool CBudgetVote::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
1370
1371
1372
1373
1374
1375
{
    // Choose coins to use
    CPubKey pubKeyCollateralAddress;
    CKey keyCollateralAddress;

    std::string errorMessage;
1376
1377
    std::string strMessage = vin.prevout.ToStringShort() + nProposalHash.ToString() + boost::lexical_cast<std::string>(nVote) + boost::lexical_cast<std::string>(nTime);

UdjinM6's avatar
UdjinM6 committed
1378
1379
1380
1381
    if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) {
        LogPrintf("CBudgetVote::Sign - Error upon calling SignMessage");
        return false;
    }
1382

UdjinM6's avatar
UdjinM6 committed
1383
1384
1385
1386
    if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) {
        LogPrintf("CBudgetVote::Sign - Error upon calling VerifyMessage");
        return false;
    }
1387
1388
1389
1390

    return true;
}

1391
bool CBudgetVote::SignatureValid(bool fSignatureCheck)
1392
1393
{
    std::string errorMessage;
1394
    std::string strMessage = vin.prevout.ToStringShort() + nProposalHash.ToString() + boost::lexical_cast<std::string>(nVote) + boost::lexical_cast<std::string>(nTime);
1395
1396
1397
1398
1399

    CMasternode* pmn = mnodeman.Find(vin);

    if(pmn == NULL)
    {
UdjinM6's avatar
UdjinM6 committed
1400
        LogPrintf("CBudgetVote::SignatureValid() - Unknown Masternode - %s\n", vin.ToString());
1401
1402
1403
        return false;
    }

1404
1405
    if(!fSignatureCheck) return true;

1406
    if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) {
1407
        LogPrintf("CBudgetVote::SignatureValid() - Verify message failed\n");
1408
1409
1410
1411
1412
1413
        return false;
    }

    return true;
}

1414
CFinalizedBudget::CFinalizedBudget()
1415
{
1416
1417
    strBudgetName = "";
    nBlockStart = 0;
UdjinM6's avatar
UdjinM6 committed
1418
    vecBudgetPayments.clear();
1419
    mapVotes.clear();
Evan Duffield's avatar
Evan Duffield committed
1420
    nFeeTXHash = 0;
1421
    fValid = true;
1422
}
1423

1424
1425
1426
1427
CFinalizedBudget::CFinalizedBudget(const CFinalizedBudget& other)
{
    strBudgetName = other.strBudgetName;
    nBlockStart = other.nBlockStart;
UdjinM6's avatar
UdjinM6 committed
1428
    vecBudgetPayments = other.vecBudgetPayments;
1429
    mapVotes = other.mapVotes;
Evan Duffield's avatar
Evan Duffield committed
1430
    nFeeTXHash = other.nFeeTXHash;
1431
    fValid = true;
1432
1433
}

1434
bool CFinalizedBudget::AddOrUpdateVote(CFinalizedBudgetVote& vote, std::string& strError)
1435
{
1436
    LOCK(cs);
1437

1438
    uint256 hash = vote.vin.prevout.GetHash();
Evan Duffield's avatar
Evan Duffield committed
1439
1440
    if(mapVotes.count(hash)){
        if(mapVotes[hash].nTime > vote.nTime){
1441
1442
1443
1444
1445
1446
1447
            strError = strprintf("new vote older than existing vote - %s\n", vote.GetHash().ToString());
            if(fDebug) LogPrintf("CFinalizedBudget::AddOrUpdateVote - %s\n", strError);
            return false;
        }
        if(vote.nTime - mapVotes[hash].nTime < BUDGET_VOTE_UPDATE_MIN){
            strError = strprintf("time between votes is too soon - %s - %lli\n", vote.GetHash().ToString(), vote.nTime - mapVotes[hash].nTime);
            if(fDebug) LogPrintf("CFinalizedBudget::AddOrUpdateVote - %s\n", strError);
Evan Duffield's avatar
Evan Duffield committed
1448
            return false;
Evan Duffield's avatar
Evan Duffield committed
1449
1450
1451
        }
    }

1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
    //if we're synced, the vote should have been recent
    if(masternodeSync.IsSynced()) {
        //up to an hour ago
        if(vote.nTime < GetTime() - (60*60)){
            strError = strprintf("new vote is too old - %s - nTime %lli - Min Time %lli\n", vote.GetHash().ToString(), vote.nTime, GetTime() - (60*60));
            if(fDebug) LogPrintf("CFinalizedBudget::AddOrUpdateVote - %s\n", strError);
            return false;
        }        
    }

    if(vote.nTime > GetTime() + (60*60)){
        strError = strprintf("new vote is too far ahead of current time - %s - nTime %lli - Max Time %lli\n", vote.GetHash().ToString(), vote.nTime, GetTime() + (60*60));
        if(fDebug) LogPrintf("CFinalizedBudget::AddOrUpdateVote - %s\n", strError);
        return false;
    }

1468
    mapVotes[hash] = vote;
Evan Duffield's avatar
Evan Duffield committed
1469
    return true;
1470
1471
}

1472
1473
//evaluate if we should vote for this. Masternode only
void CFinalizedBudget::AutoCheck()
1474
{
1475
    if(!fMasterNode || fAutoChecked) return;
1476

UdjinM6's avatar
UdjinM6 committed
1477
1478
1479
    //do this 1 in 20 blocks -- spread out the voting activity on mainnet
    if(Params().NetworkID() == CBaseChainParams::MAIN && rand() % 20 != 0) return;

1480
    fAutoChecked = true; //we only need to check this once
1481

1482
1483
    if(strBudgetMode == "auto") //only vote for exact matches
    {
UdjinM6's avatar
UdjinM6 committed
1484
        std::vector<CBudgetProposal*> vBudgetProposals = budget.GetBudget();
1485

UdjinM6's avatar
UdjinM6 committed
1486
        if(vBudgetProposals.size() == 0) {
1487
1488
1489
1490
            LogPrintf("CFinalizedBudget::AutoCheck - Can't get Budget, aborting\n");
            return;
        }

UdjinM6's avatar
UdjinM6 committed
1491
1492
        for(unsigned int i = 0; i < vecBudgetPayments.size(); i++){
            if(i > vBudgetProposals.size() - 1) {
1493
1494
1495
                LogPrintf("CFinalizedBudget::AutoCheck - Vector size mismatch, aborting\n");
                return;
            }
1496

UdjinM6's avatar
UdjinM6 committed
1497
1498
            if(vecBudgetPayments[i].nProposalHash != vBudgetProposals[i]->GetHash()){
                LogPrintf("CFinalizedBudget::AutoCheck - item #%d doesn't match %s %s\n", i, vecBudgetPayments[i].nProposalHash.ToString(), vBudgetProposals[i]->GetHash().ToString());
1499
1500
                return;
            }
1501

UdjinM6's avatar
UdjinM6 committed
1502
1503
            if(vecBudgetPayments[i].payee != vBudgetProposals[i]->GetPayee()){
                LogPrintf("CFinalizedBudget::AutoCheck - item #%d payee doesn't match %s %s\n", i, vecBudgetPayments[i].payee.ToString(), vBudgetProposals[i]->GetPayee().ToString());
1504
1505
                return;
            }
1506

UdjinM6's avatar
UdjinM6 committed
1507
1508
            if(vecBudgetPayments[i].nAmount != vBudgetProposals[i]->GetAmount()){
                LogPrintf("CFinalizedBudget::AutoCheck - item #%d payee doesn't match %s %s\n", i, vecBudgetPayments[i].payee.ToString(), vBudgetProposals[i]->GetPayee().ToString());
1509
1510
                return;
            }
1511

1512
1513
1514
            LogPrintf("CFinalizedBudget::AutoCheck - Finalized Budget Matches! Submitting Vote.\n");
            SubmitVote();
        }
1515

1516
1517
    }
}
1518
// If masternode voted for a proposal, but is now invalid -- remove the vote
1519
void CFinalizedBudget::CleanAndRemove(bool fSignatureCheck)
1520
1521
1522
1523
{
    std::map<uint256, CFinalizedBudgetVote>::iterator it = mapVotes.begin();

    while(it != mapVotes.end()) {
1524
        (*it).second.fValid = (*it).second.SignatureValid(fSignatureCheck);
1525
        ++it;
1526
1527
1528
    }
}

1529

UdjinM6's avatar
UdjinM6 committed
1530
CAmount CFinalizedBudget::GetTotalPayout()
1531
{
UdjinM6's avatar
UdjinM6 committed
1532
    CAmount ret = 0;
1533

UdjinM6's avatar
UdjinM6 committed
1534
1535
    for(unsigned int i = 0; i < vecBudgetPayments.size(); i++){
        ret += vecBudgetPayments[i].nAmount;
1536
1537
1538
1539
1540
    }

    return ret;
}

1541
std::string CFinalizedBudget::GetProposals() {
Evan Duffield's avatar
Evan Duffield committed
1542
    std::string ret = "";
1543

UdjinM6's avatar
UdjinM6 committed
1544
1545
    BOOST_FOREACH(CTxBudgetPayment& budgetPayment, vecBudgetPayments){
        CBudgetProposal* pbudgetProposal = budget.FindProposal(budgetPayment.nProposalHash);
1546

UdjinM6's avatar
UdjinM6 committed
1547
        std::string token = budgetPayment.nProposalHash.ToString();
1548

1549
        if(pbudgetProposal) token = pbudgetProposal->GetName();
1550
        if(ret == "") {ret = token;}
1551
        else {ret += "," + token;}
1552
1553
    }
    return ret;
1554
1555
}

1556
std::string CFinalizedBudget::GetStatus()
1557
{
1558
1559
    std::string retBadHashes = "";
    std::string retBadPayeeOrAmount = "";
1560

1561
1562
    for(int nBlockHeight = GetBlockStart(); nBlockHeight <= GetBlockEnd(); nBlockHeight++)
    {
UdjinM6's avatar
UdjinM6 committed
1563
1564
        CTxBudgetPayment budgetPayment;
        if(!GetBudgetPaymentByBlock(nBlockHeight, budgetPayment)){
1565
1566
1567
            LogPrintf("CFinalizedBudget::GetStatus - Couldn't find budget payment for block %lld\n", nBlockHeight);
            continue;
        }
1568

UdjinM6's avatar
UdjinM6 committed
1569
1570
        CBudgetProposal* pbudgetProposal =  budget.FindProposal(budgetPayment.nProposalHash);
        if(!pbudgetProposal){
1571
            if(retBadHashes == ""){
UdjinM6's avatar
UdjinM6 committed
1572
                retBadHashes = "Unknown proposal hash! Check this proposal before voting" + budgetPayment.nProposalHash.ToString();
1573
            } else {
UdjinM6's avatar
UdjinM6 committed
1574
                retBadHashes += "," + budgetPayment.nProposalHash.ToString();
1575
1576
            }
        } else {
UdjinM6's avatar
UdjinM6 committed
1577
            if(pbudgetProposal->GetPayee() != budgetPayment.payee || pbudgetProposal->GetAmount() != budgetPayment.nAmount)
1578
1579
            {
                if(retBadPayeeOrAmount == ""){
UdjinM6's avatar
UdjinM6 committed
1580
                    retBadPayeeOrAmount = "Budget payee/nAmount doesn't match our proposal! " + budgetPayment.nProposalHash.ToString();
1581
                } else {
UdjinM6's avatar
UdjinM6 committed
1582
                    retBadPayeeOrAmount += "," + budgetPayment.nProposalHash.ToString();
1583
1584
1585
1586
                }
            }
        }
    }
1587

1588
    if(retBadHashes == "" && retBadPayeeOrAmount == "") return "OK";
1589

1590
    return retBadHashes + retBadPayeeOrAmount;
1591
1592
}

Evan Duffield's avatar
Evan Duffield committed
1593
bool CFinalizedBudget::IsValid(std::string& strError, bool fCheckCollateral)
1594
{
1595
    //must be the correct block for payment to happen (once a month)
Evan Duffield's avatar
Evan Duffield committed
1596
1597
    if(nBlockStart % GetBudgetPaymentCycleBlocks() != 0) {strError = "Invalid BlockStart"; return false;}
    if(GetBlockEnd() - nBlockStart > 100) {strError = "Invalid BlockEnd"; return false;}
UdjinM6's avatar
UdjinM6 committed
1598
    if((int)vecBudgetPayments.size() > 100) {strError = "Invalid budget payments count (too many)"; return false;}
Evan Duffield's avatar
Evan Duffield committed
1599
1600
1601
    if(strBudgetName == "") {strError = "Invalid Budget Name"; return false;}
    if(nBlockStart == 0) {strError = "Invalid BlockStart == 0"; return false;}
    if(nFeeTXHash == 0) {strError = "Invalid FeeTx == 0"; return false;}
1602

1603
    //can only pay out 10% of the possible coins (min value of coins)
Evan Duffield's avatar
Evan Duffield committed
1604
    if(GetTotalPayout() > budget.GetTotalBudget(nBlockStart)) {strError = "Invalid Payout (more than max)"; return false;}
1605

Evan Duffield's avatar
Evan Duffield committed
1606
    std::string strError2 = "";
1607
    if(fCheckCollateral){
Evan Duffield's avatar
Evan Duffield committed
1608
1609
        if(!IsBudgetCollateralValid(nFeeTXHash, GetHash(), strError2)){
            {strError = "Invalid Collateral : " + strError2; return false;}
1610
        }
Evan Duffield's avatar
Evan Duffield committed
1611
1612
    }

1613
1614
    //TODO: if N cycles old, invalid, invalid

1615
1616
1617
    CBlockIndex* pindexPrev = chainActive.Tip();
    if(pindexPrev == NULL) return true;

Evan Duffield's avatar
Evan Duffield committed
1618
    if(nBlockStart < pindexPrev->nHeight) {strError = "Older than current blockHeight"; return false;}
UdjinM6's avatar
UdjinM6 committed
1619
    if(GetBlockEnd() < pindexPrev->nHeight - GetBudgetPaymentCycleBlocks()/2) {strError = "BlockEnd is older than blockHeight - cycle/2"; return false;}
1620

1621
    return true;
1622
1623
}

1624
bool CFinalizedBudget::IsTransactionValid(const CTransaction& txNew, int nBlockHeight)
1625
{
UdjinM6's avatar
UdjinM6 committed
1626
1627
1628
    int nCurrentBudgetPayment = nBlockHeight - GetBlockStart();
    if(nCurrentBudgetPayment < 0) {
        LogPrintf("CFinalizedBudget::IsTransactionValid - Invalid block - height: %d start: %d\n", nBlockHeight, GetBlockStart());
1629
1630
        return false;
    }
1631

UdjinM6's avatar
UdjinM6 committed
1632
1633
    if(nCurrentBudgetPayment > (int)vecBudgetPayments.size() - 1) {
        LogPrintf("CFinalizedBudget::IsTransactionValid - Invalid block - current budget payment: %d of %d\n", nCurrentBudgetPayment + 1, (int)vecBudgetPayments.size());
1634
1635
        return false;
    }
1636

1637
1638
1639
    bool found = false;
    BOOST_FOREACH(CTxOut out, txNew.vout)
    {
UdjinM6's avatar
UdjinM6 committed
1640
        if(vecBudgetPayments[nCurrentBudgetPayment].payee == out.scriptPubKey && vecBudgetPayments[nCurrentBudgetPayment].nAmount == out.nValue)
1641
1642
1643
1644
            found = true;
    }
    if(!found) {
        CTxDestination address1;
UdjinM6's avatar
UdjinM6 committed
1645
        ExtractDestination(vecBudgetPayments[nCurrentBudgetPayment].payee, address1);
1646
        CBitcoinAddress address2(address1);
1647

UdjinM6's avatar
UdjinM6 committed
1648
        LogPrintf("CFinalizedBudget::IsTransactionValid - Missing required payment - %s: %d\n", address2.ToString(), vecBudgetPayments[nCurrentBudgetPayment].nAmount);
1649
1650
1651
    }
    
    return found;
1652
1653
}

1654
void CFinalizedBudget::SubmitVote()
1655
{
1656
1657
1658
1659
1660
1661
1662
    CPubKey pubKeyMasternode;
    CKey keyMasternode;
    std::string errorMessage;

    if(!darkSendSigner.SetKey(strMasterNodePrivKey, errorMessage, keyMasternode, pubKeyMasternode)){
        LogPrintf("CFinalizedBudget::SubmitVote - Error upon calling SetKey\n");
        return;
1663
1664
    }

1665
1666
1667
1668
1669
    CFinalizedBudgetVote vote(activeMasternode.vin, GetHash());
    if(!vote.Sign(keyMasternode, pubKeyMasternode)){
        LogPrintf("CFinalizedBudget::SubmitVote - Failure to sign.");
        return;
    }
1670

1671
1672
1673
1674
1675
1676
1677
    std::string strError = "";
    if(budget.UpdateFinalizedBudget(vote, NULL, strError)){
        budget.mapSeenFinalizedBudgetVotes.insert(make_pair(vote.GetHash(), vote));
        vote.Relay();
    } else {
        LogPrintf("CFinalizedBudget::SubmitVote : Error submitting vote - %s\n", strError);
    }
1678
1679
}

1680
CFinalizedBudgetBroadcast::CFinalizedBudgetBroadcast()
1681
1682
1683
{
    strBudgetName = "";
    nBlockStart = 0;
UdjinM6's avatar
UdjinM6 committed
1684
    vecBudgetPayments.clear();
1685
    mapVotes.clear();
1686
    vchSig.clear();
Evan Duffield's avatar
Evan Duffield committed
1687
    nFeeTXHash = 0;
1688
1689
1690
1691
1692
1693
}

CFinalizedBudgetBroadcast::CFinalizedBudgetBroadcast(const CFinalizedBudget& other)
{
    strBudgetName = other.strBudgetName;
    nBlockStart = other.nBlockStart;
UdjinM6's avatar
UdjinM6 committed
1694
    BOOST_FOREACH(CTxBudgetPayment out, other.vecBudgetPayments) vecBudgetPayments.push_back(out);
1695
    mapVotes = other.mapVotes;
Evan Duffield's avatar
Evan Duffield committed
1696
    nFeeTXHash = other.nFeeTXHash;
1697
1698
}

UdjinM6's avatar
UdjinM6 committed
1699
CFinalizedBudgetBroadcast::CFinalizedBudgetBroadcast(std::string strBudgetNameIn, int nBlockStartIn, std::vector<CTxBudgetPayment> vecBudgetPaymentsIn, uint256 nFeeTXHashIn)
1700
1701
1702
{
    strBudgetName = strBudgetNameIn;
    nBlockStart = nBlockStartIn;
UdjinM6's avatar
UdjinM6 committed
1703
    BOOST_FOREACH(CTxBudgetPayment out, vecBudgetPaymentsIn) vecBudgetPayments.push_back(out);
1704
    mapVotes.clear();
Evan Duffield's avatar
Evan Duffield committed
1705
    nFeeTXHash = nFeeTXHashIn;
1706
1707
}

1708
void CFinalizedBudgetBroadcast::Relay()
1709
{
1710
    CInv inv(MSG_BUDGET_FINALIZED, GetHash());
UdjinM6's avatar
UdjinM6 committed
1711
    RelayInv(inv, MIN_BUDGET_PEER_PROTO_VERSION);
1712
1713
}

1714
CFinalizedBudgetVote::CFinalizedBudgetVote()
1715
{
1716
1717
1718
1719
    vin = CTxIn();
    nBudgetHash = 0;
    nTime = 0;
    vchSig.clear();
1720
    fValid = true;
1721
}
1722

1723
1724
1725
1726
1727
1728
CFinalizedBudgetVote::CFinalizedBudgetVote(CTxIn vinIn, uint256 nBudgetHashIn)
{
    vin = vinIn;
    nBudgetHash = nBudgetHashIn;
    nTime = GetAdjustedTime();
    vchSig.clear();
1729
    fValid = true;
1730
}
1731

1732
1733
1734
void CFinalizedBudgetVote::Relay()
{
    CInv inv(MSG_BUDGET_FINALIZED_VOTE, GetHash());
UdjinM6's avatar
UdjinM6 committed
1735
    RelayInv(inv, MIN_BUDGET_PEER_PROTO_VERSION);
1736
1737
}

1738
bool CFinalizedBudgetVote::Sign(CKey& keyMasternode, CPubKey& pubKeyMasternode)
1739
{
1740
1741
1742
    // Choose coins to use
    CPubKey pubKeyCollateralAddress;
    CKey keyCollateralAddress;
1743

1744
1745
    std::string errorMessage;
    std::string strMessage = vin.prevout.ToStringShort() + nBudgetHash.ToString() + boost::lexical_cast<std::string>(nTime);
1746

UdjinM6's avatar
UdjinM6 committed
1747
1748
1749
1750
    if(!darkSendSigner.SignMessage(strMessage, errorMessage, vchSig, keyMasternode)) {
        LogPrintf("CFinalizedBudgetVote::Sign - Error upon calling SignMessage");
        return false;
    }
1751

UdjinM6's avatar
UdjinM6 committed
1752
1753
1754
1755
    if(!darkSendSigner.VerifyMessage(pubKeyMasternode, vchSig, strMessage, errorMessage)) {
        LogPrintf("CFinalizedBudgetVote::Sign - Error upon calling VerifyMessage");
        return false;
    }
1756

1757
1758
    return true;
}
1759

1760
bool CFinalizedBudgetVote::SignatureValid(bool fSignatureCheck)
1761
{
1762
1763
    std::string errorMessage;

1764
    std::string strMessage = vin.prevout.ToStringShort() + nBudgetHash.ToString() + boost::lexical_cast<std::string>(nTime);
1765

1766
    CMasternode* pmn = mnodeman.Find(vin);
1767

1768
1769
1770
1771
    if(pmn == NULL)
    {
        LogPrintf("CFinalizedBudgetVote::SignatureValid() - Unknown Masternode\n");
        return false;
1772
1773
    }

1774
1775
    if(!fSignatureCheck) return true;

1776
1777
1778
    if(!darkSendSigner.VerifyMessage(pmn->pubkey2, vchSig, strMessage, errorMessage)) {
        LogPrintf("CFinalizedBudgetVote::SignatureValid() - Verify message failed\n");
        return false;
1779
1780
    }

1781
    return true;
1782
}
1783
1784
1785
1786
1787
1788

std::string CBudgetManager::ToString() const
{
    std::ostringstream info;

    info << "Proposals: " << (int)mapProposals.size() <<
1789
1790
1791
1792
1793
            ", Budgets: " << (int)mapFinalizedBudgets.size() <<
            ", Seen Budgets: " << (int)mapSeenMasternodeBudgetProposals.size() <<
            ", Seen Budget Votes: " << (int)mapSeenMasternodeBudgetVotes.size() <<
            ", Seen Final Budgets: " << (int)mapSeenFinalizedBudgets.size() <<
            ", Seen Final Budget Votes: " << (int)mapSeenFinalizedBudgetVotes.size();
1794
1795
1796
1797
1798

    return info.str();
}