From 31f4d5e39a524b3f274fcc36d7c8ef1b57ef44b0 Mon Sep 17 00:00:00 2001 From: Bushstar Date: Mon, 7 Aug 2023 08:24:10 +0100 Subject: [PATCH 1/3] Refactor apply/reverse general coinbase functions --- src/validation.cpp | 317 +++++++++++++++++++++++---------------------- 1 file changed, 162 insertions(+), 155 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index dff65fcef6..4b8b761369 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2046,221 +2046,228 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int height, CAmount nFees, const Consensus::Params& consensus) { + // TODO(legacy-cleanup): Clean up the rest of the method with proper structures + // and a more comprehensible flow + TAmounts const cbValues = tx.GetValuesOut(); CAmount blockReward = GetBlockSubsidy(height, consensus); if (cbValues.size() != 1 || cbValues.begin()->first != DCT_ID{0}) return Res::ErrDbg("bad-cb-wrong-tokens", "coinbase should pay only Defi coins"); + auto finalCheckAndReturn = [&](const CAmount reward) { + if (cbValues.at(DCT_ID{0}) > reward + nFees) + return Res::ErrDbg("bad-cb-amount", "coinbase pays too much (actual=%d vs limit=%d)", cbValues.at(DCT_ID{0}), blockReward + nFees); + return Res::Ok(); + }; - if (height >= consensus.AMKHeight) - { + auto logAccountChange = [](const CTransaction& tx, const CAmount subsidy, const std::string account) { + LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: hash=%s fund=%s change=%s\n", + tx.GetHash().ToString(), + account, + (CBalances{{{{0}, subsidy}}}.ToString())); + }; + + auto isUnusedEmissionFundEnabled = [](const ATTRIBUTES& attrs) { + CDataStructureV0 k{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::EmissionUnusedFund}; + return attrs.GetValue(k, false); + }; + + auto isGovernanceEnabled = [](const ATTRIBUTES& attrs) { + CDataStructureV0 k{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::GovernanceEnabled}; + return attrs.GetValue(k, false); + }; + + auto tryVerifyUtxoRewards = [](const CTransaction& tx, const CAmount blockReward, int height, const Consensus::Params& consensus) { CAmount foundationReward{0}; - if (height >= consensus.GrandCentralHeight) - { + if (height >= consensus.GrandCentralHeight) { // no foundation utxo reward check anymore - } - else if (height >= consensus.EunosHeight) - { + } else if (height >= consensus.EunosHeight) { foundationReward = CalculateCoinbaseReward(blockReward, consensus.dist.community); - } - else if (!consensus.foundationShareScript.empty() && consensus.foundationShareDFIP1) - { + } else if (!consensus.foundationShareScript.empty() && consensus.foundationShareDFIP1) { foundationReward = blockReward * consensus.foundationShareDFIP1 / COIN; } - if (foundationReward) - { + if (foundationReward) { bool foundationsRewardfound = false; - for (auto& txout : tx.vout) - { - if (txout.scriptPubKey == consensus.foundationShareScript) - { - if (txout.nValue < foundationReward) - { - return Res::ErrDbg("bad-cb-foundation-reward", "coinbase doesn't pay proper foundation reward! (actual=%d vs expected=%d", txout.nValue, foundationReward); + for (auto& txout : tx.vout) { + if (txout.scriptPubKey == consensus.foundationShareScript) { + if (txout.nValue < foundationReward) { + return Res::ErrDbg("bad-cb-foundation-reward", + "coinbase doesn't pay proper foundation reward! (actual=%d vs expected=%d", txout.nValue, foundationReward); } - foundationsRewardfound = true; break; } } - if (!foundationsRewardfound) - { + if (!foundationsRewardfound) { return Res::ErrDbg("bad-cb-foundation-reward", "coinbase doesn't pay foundation reward!"); } } + return Res::Ok(); + }; - // count and subtract for non-UTXO community rewards + auto handleLegacyTokenRewards = [&finalCheckAndReturn, &logAccountChange](const CTransaction& tx, CAmount blockReward, CCustomCSView& view, const Consensus::Params& consensus) { CAmount nonUtxoTotal = 0; - if (height >= consensus.EunosHeight) - { - CAmount subsidy; - for (const auto& kv : consensus.newNonUTXOSubsidies) - { - if (kv.first == CommunityAccountType::CommunityDevFunds) { - if (height < consensus.GrandCentralHeight) { - continue; - } - } - - subsidy = CalculateCoinbaseReward(blockReward, kv.second); + for (const auto& [accountType, accountVal] : consensus.nonUtxoBlockSubsidies) { + CAmount subsidy = blockReward * accountVal / COIN; + Res res = view.AddCommunityBalance(accountType, subsidy); + if (!res.ok) { + return Res::ErrDbg("bad-cb-community-rewards", "can't take non-UTXO community share from coinbase"); + } else { + logAccountChange(tx, subsidy, GetCommunityAccountName(accountType)); + } + nonUtxoTotal += subsidy; + } + blockReward -= nonUtxoTotal; + return finalCheckAndReturn(blockReward); + }; - Res res = Res::Ok(); + auto handleCurrentTokenRewards = [&finalCheckAndReturn, &logAccountChange, &isGovernanceEnabled, &isUnusedEmissionFundEnabled](const CTransaction& tx, CAmount blockReward, CCustomCSView& view, const Consensus::Params& consensus, int height) { + CAmount nonUtxoTotal = 0; + CAmount subsidy; - // Loan below FC and Options are unused and all go to Unallocated (burnt) pot. - if ((height < consensus.FortCanningHeight && kv.first == CommunityAccountType::Loan) || - (height < consensus.GrandCentralHeight && kv.first == CommunityAccountType::Options)) - { - res = mnview.AddCommunityBalance(CommunityAccountType::Unallocated, subsidy); - if (res) - LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: hash=%s fund=%s change=%s\n", tx.GetHash().ToString(), GetCommunityAccountName(CommunityAccountType::Unallocated), (CBalances{{{{0}, subsidy}}}.ToString())); + for (const auto& [accountType, accountVal] : consensus.newNonUTXOSubsidies) { + if (accountType == CommunityAccountType::CommunityDevFunds) { + if (height < consensus.GrandCentralHeight) { + continue; } - else - { - if (height >= consensus.GrandCentralHeight) - { - const auto attributes = mnview.GetAttributes(); - assert(attributes); - - if (kv.first == CommunityAccountType::CommunityDevFunds) { - CDataStructureV0 enabledKey{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::GovernanceEnabled}; - - if (!attributes->GetValue(enabledKey, false)) - { - res = mnview.AddBalance(consensus.foundationShareScript, {DCT_ID{0}, subsidy}); - LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: hash=%s fund=%s change=%s\n", - tx.GetHash().ToString(), ScriptToString(consensus.foundationShareScript), - (CBalances{{{{0}, subsidy}}}.ToString())); - nonUtxoTotal += subsidy; + } - continue; - } - } else if (kv.first == CommunityAccountType::Unallocated || kv.first == CommunityAccountType::Options) { - CDataStructureV0 enabledKey{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::EmissionUnusedFund}; - - if (attributes->GetValue(enabledKey, false)) { - res = mnview.AddBalance(consensus.unusedEmission, {DCT_ID{0}, subsidy}); - if (res) { - LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: hash=%s fund=%s change=%s\n", - tx.GetHash().ToString(), ScriptToString(consensus.unusedEmission), - (CBalances{{{{0}, subsidy}}}.ToString())); - } - } else { - // Previous behaviour was for Options and Unallocated to go to Unallocated - res = mnview.AddCommunityBalance(CommunityAccountType::Unallocated, subsidy); - if (res) - LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: hash=%s fund=%s change=%s\n", tx.GetHash().ToString(), GetCommunityAccountName(CommunityAccountType::Unallocated), (CBalances{{{{0}, subsidy}}}.ToString())); - } + subsidy = CalculateCoinbaseReward(blockReward, accountVal); + Res res = Res::Ok(); + // Loan below FC and Options are unused and all go to Unallocated (burnt) pot. + if ((height < consensus.FortCanningHeight && accountType == CommunityAccountType::Loan) || + (height < consensus.GrandCentralHeight && accountType == CommunityAccountType::Options)) { + res = view.AddCommunityBalance(CommunityAccountType::Unallocated, subsidy); + if (res) { + logAccountChange(tx, subsidy, GetCommunityAccountName(CommunityAccountType::Unallocated)); + } + } else { + if (height >= consensus.GrandCentralHeight) { + const auto attributes = view.GetAttributes(); + assert(attributes); + if (accountType == CommunityAccountType::CommunityDevFunds) { + if (!isGovernanceEnabled(*attributes)) { + continue; + } else { + res = view.AddBalance(consensus.foundationShareScript, {DCT_ID{0}, subsidy}); + // TODO: Result check missed; check full sync and add checks + logAccountChange(tx, subsidy, ScriptToString(consensus.foundationShareScript)); nonUtxoTotal += subsidy; - continue; } + } else if (accountType == CommunityAccountType::Unallocated || accountType == CommunityAccountType::Options) { + if (isUnusedEmissionFundEnabled(*attributes)) { + res = view.AddBalance(consensus.unusedEmission, {DCT_ID{0}, subsidy}); + if (res) { logAccountChange(tx, subsidy, ScriptToString(consensus.unusedEmission)); } + } else { + // Previous behaviour was for Options and Unallocated to go to Unallocated + res = view.AddCommunityBalance(CommunityAccountType::Unallocated, subsidy); + if (res) { logAccountChange(tx, subsidy, GetCommunityAccountName(CommunityAccountType::Unallocated)); } + } + nonUtxoTotal += subsidy; + continue; } - - res = mnview.AddCommunityBalance(kv.first, subsidy); - if (res) - LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: hash=%s fund=%s change=%s\n", tx.GetHash().ToString(), GetCommunityAccountName(kv.first), (CBalances{{{{0}, subsidy}}}.ToString())); } - if (!res.ok) - { - return Res::ErrDbg("bad-cb-community-rewards", "Cannot take non-UTXO community share from coinbase"); + res = view.AddCommunityBalance(accountType, subsidy); + if (res) { + logAccountChange(tx, subsidy, GetCommunityAccountName(accountType)); } - - nonUtxoTotal += subsidy; } - } - else - { - for (const auto& kv : consensus.nonUtxoBlockSubsidies) { - CAmount subsidy = blockReward * kv.second / COIN; - Res res = mnview.AddCommunityBalance(kv.first, subsidy); - if (!res.ok) { - return Res::ErrDbg("bad-cb-community-rewards", "can't take non-UTXO community share from coinbase"); - } else { - LogPrint(BCLog::ACCOUNTCHANGE, "AccountChange: hash=%s fund=%s change=%s\n", tx.GetHash().ToString(), GetCommunityAccountName(kv.first), (CBalances{{{{0}, subsidy}}}.ToString())); - } - nonUtxoTotal += subsidy; + + if (!res.ok) { + return Res::ErrDbg("bad-cb-community-rewards", "Cannot take non-UTXO community share from coinbase"); } + + nonUtxoTotal += subsidy; } blockReward -= nonUtxoTotal; + return finalCheckAndReturn(blockReward); + }; + + // Actual logic starts here + + if (height < consensus.AMKHeight) { + return finalCheckAndReturn(blockReward); + } + + if (auto r = tryVerifyUtxoRewards(tx, blockReward, height, consensus); !r) { + return r; } - // pre-AMK logic, compatible after prev blockReward mod: - if (cbValues.at(DCT_ID{0}) > blockReward + nFees) - return Res::ErrDbg("bad-cb-amount", "coinbase pays too much (actual=%d vs limit=%d)", cbValues.at(DCT_ID{0}), blockReward + nFees); + if (height < consensus.EunosHeight) { + return handleLegacyTokenRewards(tx, blockReward, mnview, consensus); + } - return Res::Ok(); + return handleCurrentTokenRewards(tx, blockReward, mnview, consensus, height); } void ReverseGeneralCoinbaseTx(CCustomCSView & mnview, int height, const Consensus::Params& consensus) { - CAmount blockReward = GetBlockSubsidy(height, Params().GetConsensus()); + CAmount blockReward = GetBlockSubsidy(height, consensus); - if (height >= Params().GetConsensus().AMKHeight) - { - if (height >= Params().GetConsensus().EunosHeight) - { - for (const auto& kv : Params().GetConsensus().newNonUTXOSubsidies) - { - if (kv.first == CommunityAccountType::CommunityDevFunds) { - if (height < Params().GetConsensus().GrandCentralHeight) { - continue; - } - } + auto isUnusedEmissionFundEnabled = [](const ATTRIBUTES& attrs) { + CDataStructureV0 k{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::EmissionUnusedFund}; + return attrs.GetValue(k, false); + }; - CAmount subsidy = CalculateCoinbaseReward(blockReward, kv.second); + auto isGovernanceEnabled = [](const ATTRIBUTES& attrs) { + CDataStructureV0 k{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::GovernanceEnabled}; + return attrs.GetValue(k, false); + }; - // Remove Loan and Options balances from Unallocated - if ((height < Params().GetConsensus().FortCanningHeight && kv.first == CommunityAccountType::Loan) || - (height < consensus.GrandCentralHeight && kv.first == CommunityAccountType::Options)) - { - mnview.SubCommunityBalance(CommunityAccountType::Unallocated, subsidy); - } - else - { - if (height >= consensus.GrandCentralHeight) - { - const auto attributes = mnview.GetAttributes(); - assert(attributes); + if (height < consensus.AMKHeight) { + return; + } - if (kv.first == CommunityAccountType::CommunityDevFunds) { - CDataStructureV0 enabledKey{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::GovernanceEnabled}; + // TODO(legacy-cleanup): Use proper structures - if (!attributes->GetValue(enabledKey, false)) - { - mnview.SubBalance(consensus.foundationShareScript, {DCT_ID{0}, subsidy}); + if (height < consensus.EunosHeight) { + for (const auto& [accountType, accountVal] : consensus.nonUtxoBlockSubsidies) { + CAmount subsidy = blockReward * accountVal / COIN; + mnview.SubCommunityBalance(accountType, subsidy); + } + return; + } - continue; - } - } else if (kv.first == CommunityAccountType::Unallocated || kv.first == CommunityAccountType::Options) { - CDataStructureV0 enabledKey{AttributeTypes::Param, ParamIDs::Feature, DFIPKeys::EmissionUnusedFund}; + for (const auto& [accountType, accountVal] : consensus.newNonUTXOSubsidies) { + if (accountType == CommunityAccountType::CommunityDevFunds) { + if (height < consensus.GrandCentralHeight) { + continue; + } + } - if (attributes->GetValue(enabledKey, false)) { - mnview.SubBalance(consensus.unusedEmission, {DCT_ID{0}, subsidy}); - } else { - mnview.SubCommunityBalance(CommunityAccountType::Unallocated, subsidy); - } + CAmount subsidy = CalculateCoinbaseReward(blockReward, accountVal); - continue; - } - } + // Remove Loan and Options balances from Unallocated + if ((height < consensus.FortCanningHeight && accountType == CommunityAccountType::Loan) || + (height < consensus.GrandCentralHeight && accountType == CommunityAccountType::Options)) { + mnview.SubCommunityBalance(CommunityAccountType::Unallocated, subsidy); + } else { + if (height >= consensus.GrandCentralHeight) { + const auto attributes = mnview.GetAttributes(); + assert(attributes); - mnview.SubCommunityBalance(kv.first, subsidy); + if (accountType == CommunityAccountType::CommunityDevFunds) { + if (!isGovernanceEnabled(*attributes)) { + mnview.SubBalance(consensus.foundationShareScript, {DCT_ID{0}, subsidy}); + continue; + } + } else if (accountType == CommunityAccountType::Unallocated || accountType == CommunityAccountType::Options) { + if (isUnusedEmissionFundEnabled(*attributes)) { + mnview.SubBalance(consensus.unusedEmission, {DCT_ID{0}, subsidy}); + } else { + mnview.SubCommunityBalance(CommunityAccountType::Unallocated, subsidy); + } + continue; } } - } - else - { - for (const auto& kv : Params().GetConsensus().nonUtxoBlockSubsidies) - { - CAmount subsidy = blockReward * kv.second / COIN; - mnview.SubCommunityBalance(kv.first, subsidy); - } + mnview.SubCommunityBalance(accountType, subsidy); } } } From 56f1069595229047aef6ad5c5d1c14f3d7aab2cd Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Tue, 8 Aug 2023 01:30:29 +0800 Subject: [PATCH 2/3] Align tokenRewards consensus params --- src/validation.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index bdfc21ed5b..394f965071 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2054,8 +2054,8 @@ Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int if (cbValues.size() != 1 || cbValues.begin()->first != DCT_ID{0}) return Res::ErrDbg("bad-cb-wrong-tokens", "coinbase should pay only Defi coins"); - auto finalCheckAndReturn = [&](const CAmount reward) { - if (cbValues.at(DCT_ID{0}) > reward + nFees) + auto finalCheckAndReturn = [&](const CAmount totalBlockReward) { + if (cbValues.at(DCT_ID{0}) > totalBlockReward + nFees) return Res::ErrDbg("bad-cb-amount", "coinbase pays too much (actual=%d vs limit=%d)", cbValues.at(DCT_ID{0}), blockReward + nFees); return Res::Ok(); }; @@ -2109,7 +2109,7 @@ Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int auto handleLegacyTokenRewards = [&finalCheckAndReturn, &logAccountChange](const CTransaction& tx, CAmount blockReward, CCustomCSView& view, const Consensus::Params& consensus) { CAmount nonUtxoTotal = 0; - for (const auto& [accountType, accountVal] : consensus.nonUtxoBlockSubsidies) { + for (const auto& [accountType, accountVal] : consensus.blockTokenRewardsLegacy) { CAmount subsidy = blockReward * accountVal / COIN; Res res = view.AddCommunityBalance(accountType, subsidy); if (!res.ok) { @@ -2127,7 +2127,7 @@ Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int CAmount nonUtxoTotal = 0; CAmount subsidy; - for (const auto& [accountType, accountVal] : consensus.newNonUTXOSubsidies) { + for (const auto& [accountType, accountVal] : consensus.blockTokenRewards) { if (accountType == CommunityAccountType::CommunityDevFunds) { if (height < consensus.GrandCentralHeight) { continue; @@ -2228,14 +2228,14 @@ void ReverseGeneralCoinbaseTx(CCustomCSView & mnview, int height, const Consensu // TODO(legacy-cleanup): Use proper structures if (height < consensus.EunosHeight) { - for (const auto& [accountType, accountVal] : consensus.nonUtxoBlockSubsidies) { + for (const auto& [accountType, accountVal] : consensus.blockTokenRewardsLegacy) { CAmount subsidy = blockReward * accountVal / COIN; mnview.SubCommunityBalance(accountType, subsidy); } return; } - for (const auto& [accountType, accountVal] : consensus.newNonUTXOSubsidies) { + for (const auto& [accountType, accountVal] : consensus.blockTokenRewards) { if (accountType == CommunityAccountType::CommunityDevFunds) { if (height < consensus.GrandCentralHeight) { continue; From 873dae22b52d6901c0843235e778b848ecceeac1 Mon Sep 17 00:00:00 2001 From: Bushstar Date: Tue, 8 Aug 2023 06:50:12 +0100 Subject: [PATCH 3/3] Update ApplyCoinbaseTx logic for GrandCentral --- src/validation.cpp | 8 +++----- src/validation.h | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 394f965071..7602cea7ea 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2044,7 +2044,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens return flags; } -Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int height, CAmount nFees, const Consensus::Params& consensus) +Res ApplyGeneralCoinbaseTx(CCustomCSView &mnview, const CTransaction &tx, const int height, const CAmount nFees, const Consensus::Params& consensus) { // TODO(legacy-cleanup): Clean up the rest of the method with proper structures // and a more comprehensible flow @@ -2077,7 +2077,7 @@ Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int return attrs.GetValue(k, false); }; - auto tryVerifyUtxoRewards = [](const CTransaction& tx, const CAmount blockReward, int height, const Consensus::Params& consensus) { + auto tryVerifyUtxoRewards = [](const CTransaction &tx, const CAmount blockReward, const int height, const Consensus::Params &consensus) { CAmount foundationReward{0}; if (height >= consensus.GrandCentralHeight) { // no foundation utxo reward check anymore @@ -2089,7 +2089,7 @@ Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int if (foundationReward) { bool foundationsRewardfound = false; - for (auto& txout : tx.vout) { + for (const auto &txout : tx.vout) { if (txout.scriptPubKey == consensus.foundationShareScript) { if (txout.nValue < foundationReward) { return Res::ErrDbg("bad-cb-foundation-reward", @@ -2150,8 +2150,6 @@ Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int assert(attributes); if (accountType == CommunityAccountType::CommunityDevFunds) { if (!isGovernanceEnabled(*attributes)) { - continue; - } else { res = view.AddBalance(consensus.foundationShareScript, {DCT_ID{0}, subsidy}); // TODO: Result check missed; check full sync and add checks logAccountChange(tx, subsidy, ScriptToString(consensus.foundationShareScript)); diff --git a/src/validation.h b/src/validation.h index eda0798112..5e67df3c66 100644 --- a/src/validation.h +++ b/src/validation.h @@ -848,7 +848,7 @@ inline bool IsBlockPruned(const CBlockIndex* pblockindex) return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0); } -Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int height, CAmount nFees, const Consensus::Params& consensus); +Res ApplyGeneralCoinbaseTx(CCustomCSView &mnview, const CTransaction &tx, const int height, const CAmount nFees, const Consensus::Params &consensus); void ReverseGeneralCoinbaseTx(CCustomCSView & mnview, int height, const Consensus::Params& consensus); inline CAmount CalculateCoinbaseReward(const CAmount blockReward, const uint32_t percentage)