Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Consensus] Single superblocks #2755

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/budget/budgetmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,30 @@ bool CBudgetManager::GetExpectedPayeeAmount(int chainHeight, CAmount& nAmountRet
return GetPayeeAndAmount(chainHeight, payeeRet, nAmountRet);
}

const CFinalizedBudget* CBudgetManager::GetBestFinalizedBudget(int chainHeight) const
{
if (!Params().GetConsensus().IsSuperBlock(chainHeight)) {
return nullptr;
}
int nFivePercent = mnodeman.CountEnabled() / 20;

const auto highest = GetBudgetWithHighestVoteCount(chainHeight);
if (highest.m_budget_fin == nullptr || highest.m_vote_count <= nFivePercent) {
// No finalization or not enough votes.
return nullptr;
}
return highest.m_budget_fin;
}

CAmount CBudgetManager::GetFinalizedBudgetTotalPayout(int chainHeight) const
{
const CFinalizedBudget* pfb = GetBestFinalizedBudget(chainHeight);
if (pfb == nullptr) {
return 0;
}
return pfb->GetTotalPayout();
}

bool CBudgetManager::FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const int nHeight, bool fProofOfStake) const
{
if (nHeight <= 0) return false;
Expand Down Expand Up @@ -552,6 +576,14 @@ bool CBudgetManager::FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTra
return true;
}

void CBudgetManager::FillBlockPayees(CMutableTransaction& tx, int height) const
{
const CFinalizedBudget* pfb = GetBestFinalizedBudget(height);
if (pfb != nullptr) {
pfb->PayAllBudgets(tx);
}
}

void CBudgetManager::VoteOnFinalizedBudgets()
{
// function called only from initialized masternodes
Expand Down Expand Up @@ -741,6 +773,18 @@ TrxValidationStatus CBudgetManager::IsTransactionValid(const CTransaction& txNew
return fThreshold ? TrxValidationStatus::InValid : TrxValidationStatus::VoteThreshold;
}

bool CBudgetManager::IsValidSuperBlockTx(const CTransaction& txNew, int nBlockHeight) const
{
assert(Params().GetConsensus().IsSuperBlock(nBlockHeight));

const CFinalizedBudget* pfb = GetBestFinalizedBudget(nBlockHeight);
if (pfb == nullptr) {
// No finalization or not enough votes. Nothing to check.
return true;
}
return pfb->AllBudgetsPaid(txNew);
}

std::vector<CBudgetProposal*> CBudgetManager::GetAllProposalsOrdered()
{
LOCK(cs_proposals);
Expand Down
13 changes: 9 additions & 4 deletions src/budget/budgetmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,18 +141,23 @@ class CBudgetManager : public CValidationInterface
std::vector<CBudgetProposal*> GetAllProposalsOrdered();
std::vector<CFinalizedBudget*> GetFinalizedBudgets();
bool GetExpectedPayeeAmount(int chainHeight, CAmount& nAmountRet) const;
bool IsBudgetPaymentBlock(int nBlockHeight) const;
bool IsBudgetPaymentBlock(int nBlockHeight, int& nCountThreshold) const;
CAmount GetFinalizedBudgetTotalPayout(int chainHeight) const;
bool IsBudgetPaymentBlock(int nBlockHeight) const; // legacy (multiple SB)
bool IsBudgetPaymentBlock(int nBlockHeight, int& nCountThreshold) const; // legacy (multiple SB)
bool AddProposal(CBudgetProposal& budgetProposal);
bool AddFinalizedBudget(CFinalizedBudget& finalizedBudget, CNode* pfrom = nullptr);
void ForceAddFinalizedBudget(const uint256& nHash, const uint256& feeTxId, const CFinalizedBudget& finalizedBudget);
uint256 SubmitFinalBudget();

bool UpdateProposal(const CBudgetVote& vote, CNode* pfrom, std::string& strError);
bool UpdateFinalizedBudget(const CFinalizedBudgetVote& vote, CNode* pfrom, std::string& strError);
TrxValidationStatus IsTransactionValid(const CTransaction& txNew, const uint256& nBlockHash, int nBlockHeight) const;
TrxValidationStatus IsTransactionValid(const CTransaction& txNew, const uint256& nBlockHash, int nBlockHeight) const; // legacy (multiple SB)
bool IsValidSuperBlockTx(const CTransaction& txNew, int nBlockHeight) const; // v6.0: single SB

std::string GetRequiredPaymentsString(int nBlockHeight);
bool FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const int nHeight, bool fProofOfStake) const;
const CFinalizedBudget* GetBestFinalizedBudget(int chainHeight) const;
bool FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const int nHeight, bool fProofOfStake) const; // legacy (multiple SB)
void FillBlockPayees(CMutableTransaction& tx, int height) const; // v6.0: single SB

// Only initialized masternodes: sign and submit votes on valid finalized budgets
void VoteOnFinalizedBudgets();
Expand Down
4 changes: 2 additions & 2 deletions src/budget/budgetproposal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "budget/budgetproposal.h"

#include "chainparams.h"
#include "script/standard.h"

Expand Down Expand Up @@ -85,8 +86,7 @@ bool CBudgetProposal::IsHeavilyDownvoted(int mnCount)
bool CBudgetProposal::CheckStartEnd()
{
// block start must be a superblock
if (nBlockStart < 0 ||
nBlockStart % Params().GetConsensus().nBudgetCycleBlocks != 0) {
if (nBlockStart < 0 || !Params().GetConsensus().IsSuperBlock(nBlockStart)) {
strInvalid = "Invalid nBlockStart";
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/budget/budgetutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,4 +269,4 @@ UniValue mnLocalBudgetVoteInner(bool fLegacyMN, const uint256& budgetHash, bool

return (fFinal ? voteFinalBudget(budgetHash, mnKeys, resultsObj, 0)
: voteProposal(budgetHash, nVote, mnKeys, resultsObj, 0));
}
}
42 changes: 41 additions & 1 deletion src/budget/finalizedbudget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "budget/finalizedbudget.h"

#include "masternodeman.h"
#include "utilmoneystr.h"
#include "validation.h"

CFinalizedBudget::CFinalizedBudget() :
Expand Down Expand Up @@ -187,7 +188,7 @@ bool CFinalizedBudget::CheckStartEnd()
}

// Must be the correct block for payment to happen (once a month)
if (nBlockStart % Params().GetConsensus().nBudgetCycleBlocks != 0) {
if (!Params().GetConsensus().IsSuperBlock(nBlockStart)) {
strInvalid = "Invalid BlockStart";
return false;
}
Expand Down Expand Up @@ -389,6 +390,45 @@ bool CFinalizedBudget::GetPayeeAndAmount(int64_t nBlockHeight, CScript& payee, C
return true;
}

bool CFinalizedBudget::AllBudgetsPaid(const CTransaction& tx) const
{
// make a map for faster lookup and deal with duplicate payees
struct cmp {
bool operator()(const CTxOut& a, const CTxOut& b) const {
return a.scriptPubKey < b.scriptPubKey ||
(a.scriptPubKey == b.scriptPubKey && a.nValue < b.nValue);
}
};
std::map<CTxOut, int, cmp> txouts;
for (const CTxOut& o: tx.vout) {
txouts[o]++;
}

for (const CTxBudgetPayment& payment : vecBudgetPayments) {
auto it = txouts.find(CTxOut(payment.nAmount, payment.payee));
if (it == txouts.end() || it->second == 0) {
// Payment not found
CTxDestination addr;
const std::string& payee = ExtractDestination(payment.payee, addr) ? EncodeDestination(addr)
: HexStr(payment.payee);
LogPrint(BCLog::MNBUDGET, "Missing payment of %s for %s (proposal hash: %s)\n",
FormatMoney(payment.nAmount), payee, payment.nProposalHash.ToString());
return false;
}
it->second--;
}

// all budgets are paid by tx
return true;
}

void CFinalizedBudget::PayAllBudgets(CMutableTransaction& tx) const
{
for (const CTxBudgetPayment& payment : vecBudgetPayments) {
tx.vout.emplace_back(payment.nAmount, payment.payee);
}
}

// return broadcast serialization
CDataStream CFinalizedBudget::GetBroadcast() const
{
Expand Down
6 changes: 6 additions & 0 deletions src/budget/finalizedbudget.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ class CFinalizedBudget
bool GetBudgetPaymentByBlock(int64_t nBlockHeight, CTxBudgetPayment& payment) const;
bool GetPayeeAndAmount(int64_t nBlockHeight, CScript& payee, CAmount& nAmount) const;

// Check if ALL the budgets are paid by transaction tx
bool AllBudgetsPaid(const CTransaction& tx) const;

// Add payments for ALL budgets to tx outs
void PayAllBudgets(CMutableTransaction& tx) const;

// Check finalized budget proposals. Masternodes only (when voting on finalized budgets)
bool CheckProposals(const std::map<uint256, CBudgetProposal>& mapWinningProposals) const;
// Total amount paid out by this budget
Expand Down
1 change: 1 addition & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ struct Params {
return (contextHeight - utxoFromBlockHeight >= nStakeMinDepth);
}

bool IsSuperBlock(int height) const { return height % nBudgetCycleBlocks == 0; }

/*
* (Legacy) Zerocoin consensus params
Expand Down
115 changes: 87 additions & 28 deletions src/masternode-payments.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ void DumpMasternodePayments()
LogPrint(BCLog::MASTERNODE,"Budget dump finished %dms\n", GetTimeMillis() - nStart);
}

bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CAmount& nBudgetAmt)
static bool IsBlockValueValid_legacy(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CAmount& nBudgetAmt)
{
const Consensus::Params& consensus = Params().GetConsensus();
if (!g_tiertwo_sync_state.IsSynced()) {
Expand Down Expand Up @@ -224,18 +224,39 @@ bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CA
return nMinted <= nExpectedValue;
}

bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev)
bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, CAmount& nBudgetAmt)
{
const Consensus::Params& consensus = Params().GetConsensus();
if (!consensus.NetworkUpgradeActive(nHeight, Consensus::UPGRADE_V6_0)) {
return IsBlockValueValid_legacy(nHeight, nExpectedValue, nMinted, nBudgetAmt);
}

if (consensus.IsSuperBlock(nHeight)) {
if (!g_tiertwo_sync_state.IsSynced()) {
// there is no budget data to use to check anything
nExpectedValue += g_budgetman.GetTotalBudget(nHeight);
} else if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) {
// we're synced and the superblock spork is enabled
nBudgetAmt = g_budgetman.GetFinalizedBudgetTotalPayout(nHeight);
nExpectedValue += nBudgetAmt;
}
}

return nMinted >= 0 && nMinted <= nExpectedValue;
}

static bool IsBlockPayeeValid_legacy(const CBlock& block, const CBlockIndex* pindexPrev)
{
int nBlockHeight = pindexPrev->nHeight + 1;
assert(!Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_V6_0));
TrxValidationStatus transactionStatus = TrxValidationStatus::InValid;

if (!g_tiertwo_sync_state.IsSynced()) { //there is no budget data to use to check anything -- find the longest chain
LogPrint(BCLog::MASTERNODE, "Client not synced, skipping block payee checks\n");
return true;
}

const bool fPayCoinstake = Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_POS) &&
!Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_V6_0);
const bool fPayCoinstake = Params().GetConsensus().NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_POS);
const CTransaction& txNew = *(fPayCoinstake ? block.vtx[1] : block.vtx[0]);

//check if it's a budget block
Expand Down Expand Up @@ -272,8 +293,38 @@ bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev)
return true;
}

bool IsBlockPayeeValid(const CBlock& block, const CBlockIndex* pindexPrev)
{
const Consensus::Params& consensus = Params().GetConsensus();
int nBlockHeight = pindexPrev->nHeight + 1;
if (!consensus.NetworkUpgradeActive(nBlockHeight, Consensus::UPGRADE_V6_0)) {
return IsBlockPayeeValid_legacy(block, pindexPrev);
}

void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const CBlockIndex* pindexPrev, bool fProofOfStake)
if (!g_tiertwo_sync_state.IsSynced()) { //there is no budget data to use to check anything -- find the longest chain
// !TODO: after transition to v6, restrict this to budget-checks only
LogPrint(BCLog::MASTERNODE, "Client not synced, skipping block payee checks\n");
return true;
}

const CTransaction& coinbase_tx = *block.vtx[0];

// Check masternode payment
if (sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT) &&
!masternodePayments.IsTransactionValid(coinbase_tx, pindexPrev)) {
LogPrint(BCLog::MASTERNODE, "Missing required masternode payment\n");
return false;
}

// Check budget payments during superblocks
if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && consensus.IsSuperBlock(nBlockHeight)) {
return g_budgetman.IsValidSuperBlockTx(coinbase_tx, nBlockHeight);
}

return true;
}

static void FillBlockPayee_legacy(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const CBlockIndex* pindexPrev, bool fProofOfStake)
{
if (!sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) || // if superblocks are not enabled
// ... or this is not a superblock
Expand All @@ -283,6 +334,23 @@ void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoin
}
}

void FillBlockPayee(CMutableTransaction& txCoinbase, CMutableTransaction& txCoinstake, const CBlockIndex* pindexPrev, bool fProofOfStake)
{
int height = pindexPrev->nHeight + 1;
if (!Params().GetConsensus().NetworkUpgradeActive(height, Consensus::UPGRADE_V6_0)) {
// legacy - !TODO: remove after transition
return FillBlockPayee_legacy(txCoinbase, txCoinstake, pindexPrev, fProofOfStake);
}

// Add masternode payment
masternodePayments.FillBlockPayee(txCoinbase, txCoinstake, pindexPrev, fProofOfStake);

// Add budget payments (if superblock, and SPORK_13 is active)
if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS)) {
g_budgetman.FillBlockPayees(txCoinbase, height);
}
}

std::string GetRequiredPaymentsString(int nBlockHeight)
{
if (sporkManager.IsSporkActive(SPORK_13_ENABLE_SUPERBLOCKS) && g_budgetman.IsBudgetPaymentBlock(nBlockHeight)) {
Expand Down Expand Up @@ -825,30 +893,21 @@ bool IsCoinbaseValueValid(const CTransactionRef& tx, CAmount nBudgetAmt, CValida
{
assert(tx->IsCoinBase());
if (g_tiertwo_sync_state.IsSynced()) {
const CAmount nCBaseOutAmt = tx->GetValueOut();
if (nBudgetAmt > 0) {
// Superblock
if (nCBaseOutAmt != nBudgetAmt) {
const std::string strError = strprintf("%s: invalid coinbase payment for budget (%s vs expected=%s)",
__func__, FormatMoney(nCBaseOutAmt), FormatMoney(nBudgetAmt));
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, "bad-superblock-cb-amt");
}
return true;
} else {
// regular block
CAmount nMnAmt = GetMasternodePayment();
// if enforcement is disabled, there could be no masternode payment
bool sporkEnforced = sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT);
const std::string strError = strprintf("%s: invalid coinbase payment for masternode (%s vs expected=%s)",
__func__, FormatMoney(nCBaseOutAmt), FormatMoney(nMnAmt));
if (sporkEnforced && nCBaseOutAmt != nMnAmt) {
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, "bad-cb-amt");
}
if (!sporkEnforced && nCBaseOutAmt > nMnAmt) {
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, "bad-cb-amt-spork8-disabled");
}
return true;
const CAmount paid = tx->GetValueOut();
const CAmount expected = GetMasternodePayment() + nBudgetAmt;
// if enforcement is disabled, there could be no masternode payment
bool sporkEnforced = sporkManager.IsSporkActive(SPORK_8_MASTERNODE_PAYMENT_ENFORCEMENT);

const std::string strError = strprintf("%s: invalid coinbase payment (%s vs expected=%s)",
__func__, FormatMoney(paid), FormatMoney(expected));
std::string rej_reason = (nBudgetAmt > 0 ? "bad-superblock-cb-amt" : "bad-cb-amt");
if (sporkEnforced && paid != expected) {
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, rej_reason);
}
if (!sporkEnforced && paid > expected) {
return _state.DoS(100, error(strError.c_str()), REJECT_INVALID, rej_reason+"-spork8-disabled");
}
return true;
}
return true;
}
Loading