diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c4878e7b7fe..4b1f3f95634 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -207,8 +207,8 @@ class CMainParams : public CChainParams { consensus.props.emergencyPeriod = 8640; consensus.props.feeBurnPct = COIN / 2; - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::IncentiveFunding, 45 * COIN / 200); // 45 DFI of 200 per block (rate normalized to (COIN == 100%)) - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::AnchorReward, COIN /10 / 200); // 0.1 DFI of 200 per block + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::IncentiveFunding, 45 * COIN / 200); // 45 DFI of 200 per block (rate normalized to (COIN == 100%)) + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::AnchorReward, COIN /10 / 200); // 0.1 DFI of 200 per block // New coinbase reward distribution consensus.dist.masternode = 3333; // 33.33% @@ -219,12 +219,12 @@ class CMainParams : public CChainParams { consensus.dist.options = 988; // 9.88% consensus.dist.unallocated = 173; // 1.73% - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Loan, consensus.dist.loan); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Options, consensus.dist.options); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + consensus.blockTokenRewards.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); + consensus.blockTokenRewards.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); + consensus.blockTokenRewards.emplace(CommunityAccountType::Loan, consensus.dist.loan); + consensus.blockTokenRewards.emplace(CommunityAccountType::Options, consensus.dist.options); + consensus.blockTokenRewards.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); + consensus.blockTokenRewards.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); // EVM chain id consensus.evmChainId = 1130; // ETH main chain ID @@ -483,8 +483,8 @@ class CTestNetParams : public CChainParams { consensus.props.feeBurnPct = COIN / 2; - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::IncentiveFunding, 45 * COIN / 200); // 45 DFI @ 200 per block (rate normalized to (COIN == 100%)) - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::AnchorReward, COIN/10 / 200); // 0.1 DFI @ 200 per block + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::IncentiveFunding, 45 * COIN / 200); // 45 DFI @ 200 per block (rate normalized to (COIN == 100%)) + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::AnchorReward, COIN/10 / 200); // 0.1 DFI @ 200 per block // New coinbase reward distribution consensus.dist.masternode = 3333; // 33.33% @@ -495,12 +495,12 @@ class CTestNetParams : public CChainParams { consensus.dist.options = 988; // 9.88% consensus.dist.unallocated = 173; // 1.73% - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Loan, consensus.dist.loan); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Options, consensus.dist.options); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + consensus.blockTokenRewards.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); + consensus.blockTokenRewards.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); + consensus.blockTokenRewards.emplace(CommunityAccountType::Loan, consensus.dist.loan); + consensus.blockTokenRewards.emplace(CommunityAccountType::Options, consensus.dist.options); + consensus.blockTokenRewards.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); + consensus.blockTokenRewards.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); // EVM chain id consensus.evmChainId = 1131; // test chain ID @@ -698,8 +698,8 @@ class CChangiParams : public CChainParams { consensus.props.emergencyPeriod = 8640; consensus.props.feeBurnPct = COIN / 2; - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::IncentiveFunding, 45 * COIN / 200); // 45 DFI @ 200 per block (rate normalized to (COIN == 100%)) - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::AnchorReward, COIN/10 / 200); // 0.1 DFI @ 200 per block + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::IncentiveFunding, 45 * COIN / 200); // 45 DFI @ 200 per block (rate normalized to (COIN == 100%)) + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::AnchorReward, COIN/10 / 200); // 0.1 DFI @ 200 per block // New coinbase reward distribution consensus.dist.masternode = 3333; // 33.33% @@ -710,12 +710,12 @@ class CChangiParams : public CChainParams { consensus.dist.options = 988; // 9.88% consensus.dist.unallocated = 173; // 1.73% - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Loan, consensus.dist.loan); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Options, consensus.dist.options); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + consensus.blockTokenRewards.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); + consensus.blockTokenRewards.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); + consensus.blockTokenRewards.emplace(CommunityAccountType::Loan, consensus.dist.loan); + consensus.blockTokenRewards.emplace(CommunityAccountType::Options, consensus.dist.options); + consensus.blockTokenRewards.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); + consensus.blockTokenRewards.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); // EVM chain id consensus.evmChainId = 1133; // changi chain ID @@ -914,8 +914,8 @@ class CDevNetParams : public CChainParams { consensus.props.emergencyPeriod = 8640; consensus.props.feeBurnPct = COIN / 2; - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::IncentiveFunding, 45 * COIN / 200); // 45 DFI @ 200 per block (rate normalized to (COIN == 100%)) - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::AnchorReward, COIN/10 / 200); // 0.1 DFI @ 200 per block + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::IncentiveFunding, 45 * COIN / 200); // 45 DFI @ 200 per block (rate normalized to (COIN == 100%)) + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::AnchorReward, COIN/10 / 200); // 0.1 DFI @ 200 per block // New coinbase reward distribution consensus.dist.masternode = 3333; // 33.33% @@ -926,12 +926,12 @@ class CDevNetParams : public CChainParams { consensus.dist.options = 988; // 9.88% consensus.dist.unallocated = 173; // 1.73% - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Loan, consensus.dist.loan); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Options, consensus.dist.options); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + consensus.blockTokenRewards.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); + consensus.blockTokenRewards.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); + consensus.blockTokenRewards.emplace(CommunityAccountType::Loan, consensus.dist.loan); + consensus.blockTokenRewards.emplace(CommunityAccountType::Options, consensus.dist.options); + consensus.blockTokenRewards.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); + consensus.blockTokenRewards.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); // EVM chain id consensus.evmChainId = 1132; // dev chain ID @@ -1133,8 +1133,8 @@ class CRegTestParams : public CChainParams { consensus.vaultCreationFee = 1 * COIN; - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::IncentiveFunding, 10 * COIN / 50); // normalized to (COIN == 100%) // 10 per block - consensus.nonUtxoBlockSubsidies.emplace(CommunityAccountType::AnchorReward, COIN/10 / 50); // 0.1 per block + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::IncentiveFunding, 10 * COIN / 50); // normalized to (COIN == 100%) // 10 per block + consensus.blockTokenRewardsLegacy.emplace(CommunityAccountType::AnchorReward, COIN/10 / 50); // 0.1 per block // New coinbase reward distribution consensus.dist.masternode = 3333; // 33.33% @@ -1145,12 +1145,12 @@ class CRegTestParams : public CChainParams { consensus.dist.options = 988; // 9.88% consensus.dist.unallocated = 173; // 1.73% - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Loan, consensus.dist.loan); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Options, consensus.dist.options); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); - consensus.newNonUTXOSubsidies.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); + consensus.blockTokenRewards.emplace(CommunityAccountType::AnchorReward, consensus.dist.anchor); + consensus.blockTokenRewards.emplace(CommunityAccountType::IncentiveFunding, consensus.dist.liquidity); + consensus.blockTokenRewards.emplace(CommunityAccountType::Loan, consensus.dist.loan); + consensus.blockTokenRewards.emplace(CommunityAccountType::Options, consensus.dist.options); + consensus.blockTokenRewards.emplace(CommunityAccountType::Unallocated, consensus.dist.unallocated); + consensus.blockTokenRewards.emplace(CommunityAccountType::CommunityDevFunds, consensus.dist.community); // EVM chain id consensus.evmChainId = 1133; // regtest chain ID diff --git a/src/consensus/params.h b/src/consensus/params.h index 5b21801430f..f5393789e31 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -221,8 +221,8 @@ struct Params { }; CProposalParams props; - std::map nonUtxoBlockSubsidies; - std::map newNonUTXOSubsidies; + std::map blockTokenRewardsLegacy; + std::map blockTokenRewards; uint64_t evmChainId; }; diff --git a/src/core_io.h b/src/core_io.h index f0cd289bc49..0bcae456c89 100644 --- a/src/core_io.h +++ b/src/core_io.h @@ -46,6 +46,6 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0); std::string SighashToStr(unsigned char sighash_type); void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); void ScriptToUniv(const CScript& script, UniValue& out, bool include_address); -void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0); +void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, int verbosity = 0); #endif // DEFI_CORE_IO_H diff --git a/src/core_write.cpp b/src/core_write.cpp index 12a089e6479..dd934d26e97 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -183,19 +183,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, out.pushKV("addresses", a); } -void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags) +void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, int version) { - entry.pushKV("txid", tx.GetHash().GetHex()); - entry.pushKV("hash", tx.GetWitnessHash().GetHex()); - entry.pushKV("version", tx.nVersion); - entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION)); - entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR); - entry.pushKV("weight", GetTransactionWeight(tx)); - entry.pushKV("locktime", (int64_t)tx.nLockTime); - - UniValue vin(UniValue::VARR); - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const CTxIn& txin = tx.vin[i]; + const auto txInToUniValue = [](const CTransaction& tx, const CTxIn& txin, const unsigned int index, bool include_hex) { UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); @@ -204,38 +194,54 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, in.pushKV("vout", (int64_t)txin.prevout.n); UniValue o(UniValue::VOBJ); o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true)); - o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); + if (include_hex) { + o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())); + } in.pushKV("scriptSig", o); - if (!tx.vin[i].scriptWitness.IsNull()) { + if (!tx.vin[index].scriptWitness.IsNull()) { UniValue txinwitness(UniValue::VARR); - for (const auto& item : tx.vin[i].scriptWitness.stack) { + for (const auto& item : tx.vin[index].scriptWitness.stack) { txinwitness.push_back(HexStr(item.begin(), item.end())); } in.pushKV("txinwitness", txinwitness); } } in.pushKV("sequence", (int64_t)txin.nSequence); - vin.push_back(in); - } - entry.pushKV("vin", vin); - - UniValue vout(UniValue::VARR); - for (unsigned int i = 0; i < tx.vout.size(); i++) { - const CTxOut& txout = tx.vout[i]; + return in; + }; + const auto txOutToUniValue = [](const CTransaction& tx, const CTxOut& txout, const unsigned int index, bool include_hex, int version) { UniValue out(UniValue::VOBJ); - out.pushKV("value", ValueFromAmount(txout.nValue)); - out.pushKV("n", (int64_t)i); - + out.pushKV("n", (int64_t)index); UniValue o(UniValue::VOBJ); - ScriptPubKeyToUniv(txout.scriptPubKey, o, true); + ScriptPubKeyToUniv(txout.scriptPubKey, o, include_hex); out.pushKV("scriptPubKey", o); - // Start to print tokenId start from version TOKENS_MIN_VERSION - if (tx.nVersion >= CTransaction::TOKENS_MIN_VERSION) { + // We skip this for v3+ as we tokenId field is unused. + if (version <= 2 && tx.nVersion >= CTransaction::TOKENS_MIN_VERSION) { + // Start to print tokenId start from version TOKENS_MIN_VERSION out.pushKV("tokenId", (uint64_t)txout.nTokenId.v); } - vout.push_back(out); + return out; + }; + + entry.pushKV("txid", tx.GetHash().GetHex()); + entry.pushKV("hash", tx.GetWitnessHash().GetHex()); + entry.pushKV("version", tx.nVersion); + entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION)); + entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR); + entry.pushKV("weight", GetTransactionWeight(tx)); + entry.pushKV("locktime", (int64_t)tx.nLockTime); + + UniValue vin(UniValue::VARR); + for (unsigned int i = 0; i < tx.vin.size(); i++) { + vin.push_back(txInToUniValue(tx, tx.vin[i], i, include_hex)); + } + entry.pushKV("vin", vin); + + UniValue vout(UniValue::VARR); + for (unsigned int i = 0; i < tx.vout.size(); i++) { + vout.push_back(txOutToUniValue(tx, tx.vout[i], i, include_hex, version)); } entry.pushKV("vout", vout); diff --git a/src/masternodes/incentivefunding.h b/src/masternodes/incentivefunding.h index 0fdcfe406c5..f596f374ff7 100644 --- a/src/masternodes/incentivefunding.h +++ b/src/masternodes/incentivefunding.h @@ -18,7 +18,7 @@ inline CommunityAccountType CommunityAccountCodeToType(unsigned char ch) { return CommunityAccountType::None; } -inline const char *GetCommunityAccountName(CommunityAccountType t) { +inline const std::string GetCommunityAccountName(const CommunityAccountType t) { switch (t) { case CommunityAccountType::IncentiveFunding: return "IncentiveFunding"; diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index c92edb95e4e..2561afeca04 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -5230,6 +5230,45 @@ bool IsTransferDomainEnabled(const int height, const CCustomCSView &view, const return attributes->GetValue(enabledKey, false); } +UniValue EVM::ToUniValue() const { + UniValue obj(UniValue::VOBJ); + obj.pushKV("version", static_cast(version)); + obj.pushKV("blockHash", "0x" + blockHash.GetHex()); + obj.pushKV("burntFee", burntFee); + obj.pushKV("priorityFee", priorityFee); + return obj; +} + +UniValue XVM::ToUniValue() const { + UniValue obj(UniValue::VOBJ); + obj.pushKV("version", static_cast(version)); + obj.pushKV("evm", evm.ToUniValue()); + return obj; +} + +ResVal XVM::TryFrom(const CScript &scriptPubKey) { + opcodetype opcode; + auto pc = scriptPubKey.begin(); + if (!scriptPubKey.GetOp(pc, opcode) || opcode != OP_RETURN) { + return Res::Err("Coinbase XVM: OP_RETURN expected"); + } + + std::vector metadata; + if (!scriptPubKey.GetOp(pc, opcode, metadata) + || (opcode > OP_PUSHDATA1 && opcode != OP_PUSHDATA2 && opcode != OP_PUSHDATA4)) { + return Res::Err("Coinbase XVM: OP_PUSHDATA expected"); + } + + XVM obj; + try { + CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); + ss >> obj; + } catch (...) { + return Res::Err("Coinbase XVM: Deserialization failed"); + } + return { obj, Res::Ok() }; +} + OpReturnLimits OpReturnLimits::Default() { return OpReturnLimits { @@ -5264,22 +5303,26 @@ void OpReturnLimits::SetToAttributesIfNotExists(ATTRIBUTES& attrs) const { } Res OpReturnLimits::Validate(const CTransaction& tx, const CustomTxType txType) const { + auto err = [](const std::string area, const int voutIndex) { + return Res::ErrCode(CustomTxErrCodes::Fatal, "OP_RETURN size check: vout[%d] %s failure", voutIndex, area); + }; + // Check core OP_RETURN size on vout[0] if (txType == CustomTxType::EvmTx) { if (!CheckOPReturnSize(tx.vout[0].scriptPubKey, evmSizeBytes)) { - return Res::ErrCode(CustomTxErrCodes::Fatal, "Invalid EVM OP_RETURN data size"); + return err("EVM", 0); } } else if (txType != CustomTxType::None) { if (!CheckOPReturnSize(tx.vout[0].scriptPubKey, dvmSizeBytes)) { - return Res::ErrCode(CustomTxErrCodes::Fatal, "Invalid DVM OP_RETURN data size"); + return err("DVM", 0); } } else if (!CheckOPReturnSize(tx.vout[0].scriptPubKey, coreSizeBytes)) { - return Res::ErrCode(CustomTxErrCodes::Fatal, "Invalid core OP_RETURN data size"); + return err("Core", 0); } // Check core OP_RETURN size on vout[1] and higher outputs for (size_t i{1}; i < tx.vout.size(); ++i) { if (!CheckOPReturnSize(tx.vout[i].scriptPubKey, coreSizeBytes)) { - return Res::ErrCode(CustomTxErrCodes::Fatal, "Invalid core OP_RETURN data size"); + return err("Core", i); } } return Res::Ok(); diff --git a/src/masternodes/mn_checks.h b/src/masternodes/mn_checks.h index ecf31d8f8c7..b564d929a24 100644 --- a/src/masternodes/mn_checks.h +++ b/src/masternodes/mn_checks.h @@ -37,6 +37,8 @@ struct EVM { READWRITE(burntFee); READWRITE(priorityFee); } + + UniValue ToUniValue() const; }; struct XVM { @@ -51,6 +53,10 @@ struct XVM { READWRITE(version); READWRITE(evm); } + + static ResVal TryFrom(const CScript &scriptPubKey); + UniValue ToUniValue() const; + }; class CCustomTxVisitor { diff --git a/src/masternodes/rpc_accounts.cpp b/src/masternodes/rpc_accounts.cpp index e24366a8b34..b51efeb4f35 100644 --- a/src/masternodes/rpc_accounts.cpp +++ b/src/masternodes/rpc_accounts.cpp @@ -1830,7 +1830,7 @@ UniValue listcommunitybalances(const JSONRPCRequest& request) { LOCK(cs_main); CAmount burnt{0}; - for (const auto& kv : Params().GetConsensus().newNonUTXOSubsidies) + for (const auto& kv : Params().GetConsensus().blockTokenRewards) { // Skip these as any unused balance will be burnt. if (kv.first == CommunityAccountType::Options) { @@ -2218,7 +2218,7 @@ UniValue getburninfo(const JSONRPCRequest& request) { dfiToDUSDTokens = attributes->GetValue(liveKey, CBalances{}); } - for (const auto& kv : Params().GetConsensus().newNonUTXOSubsidies) { + for (const auto& kv : Params().GetConsensus().blockTokenRewards) { if (kv.first == CommunityAccountType::Unallocated || kv.first == CommunityAccountType::IncentiveFunding || (height >= fortCanningHeight && kv.first == CommunityAccountType::Loan)) { diff --git a/src/masternodes/validation.cpp b/src/masternodes/validation.cpp index eb27b160d79..026641a392e 100644 --- a/src/masternodes/validation.cpp +++ b/src/masternodes/validation.cpp @@ -25,7 +25,7 @@ template static void UpdateDailyGovVariables(const std::map::const_iterator& incentivePair, CCustomCSView& cache, int nHeight) { - if (incentivePair != Params().GetConsensus().newNonUTXOSubsidies.end()) + if (incentivePair != Params().GetConsensus().blockTokenRewards.end()) { CAmount subsidy = CalculateCoinbaseReward(GetBlockSubsidy(nHeight, Params().GetConsensus()), incentivePair->second); subsidy *= Params().GetConsensus().blocksPerDay(); @@ -48,14 +48,14 @@ static void ProcessRewardEvents(const CBlockIndex* pindex, CCustomCSView& cache, // Hard coded LP_DAILY_DFI_REWARD change if (pindex->nHeight >= chainparams.GetConsensus().EunosHeight) { - const auto& incentivePair = chainparams.GetConsensus().newNonUTXOSubsidies.find(CommunityAccountType::IncentiveFunding); + const auto& incentivePair = chainparams.GetConsensus().blockTokenRewards.find(CommunityAccountType::IncentiveFunding); UpdateDailyGovVariables(incentivePair, cache, pindex->nHeight); } // Hard coded LP_DAILY_LOAN_TOKEN_REWARD change if (pindex->nHeight >= chainparams.GetConsensus().FortCanningHeight) { - const auto& incentivePair = chainparams.GetConsensus().newNonUTXOSubsidies.find(CommunityAccountType::Loan); + const auto& incentivePair = chainparams.GetConsensus().blockTokenRewards.find(CommunityAccountType::Loan); UpdateDailyGovVariables(incentivePair, cache, pindex->nHeight); } @@ -2380,29 +2380,13 @@ static void RevertFailedTransferDomainTxs(const std::vector &failed static Res ValidateCoinbaseXVMOutput(const CScript &scriptPubKey, const FinalizeBlockCompletion &blockResult) { const auto coinbaseBlockHash = uint256(std::vector(blockResult.block_hash.begin(), blockResult.block_hash.end())); - - // Mine does not add output on null block + // Miner does not add output on null block if (coinbaseBlockHash.IsNull()) return Res::Ok(); - opcodetype opcode; - auto pc = scriptPubKey.begin(); - if (!scriptPubKey.GetOp(pc, opcode) || opcode != OP_RETURN) { - return Res::Err("Coinbase output does not contain OP_RETURN as expected"); - } - - std::vector metadata; - if (!scriptPubKey.GetOp(pc, opcode, metadata) - || (opcode > OP_PUSHDATA1 && opcode != OP_PUSHDATA2 && opcode != OP_PUSHDATA4)) { - return Res::Err("Coinbase OP_RETURN output missing push data"); - } - - XVM obj; - try { - CDataStream ss(metadata, SER_NETWORK, PROTOCOL_VERSION); - ss >> obj; - } catch (...) { - return Res::Err("Failed to deserialize coinbase output"); - } + auto res = XVM::TryFrom(scriptPubKey); + if (!res.ok) return res; + + auto obj = *res; if (obj.evm.blockHash != coinbaseBlockHash) { return Res::Err("Incorrect EVM block hash in coinbase output"); diff --git a/src/miner.cpp b/src/miner.cpp index bc67a4f3d64..0677a536933 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -407,7 +407,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc __func__, blockReward, coinbaseTx.vout[0].nValue, foundationValue); } else if (nHeight >= consensus.AMKHeight) { // assume community non-utxo funding: - for (const auto& kv : consensus.nonUtxoBlockSubsidies) { + for (const auto& kv : consensus.blockTokenRewardsLegacy) { coinbaseTx.vout[0].nValue -= blockReward * kv.second / COIN; } // Pinch off foundation share diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 767b75de4ea..e7159b43e32 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -117,10 +117,214 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex return result; } -UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails) +struct MinterInfo { + std::string Id{}; + std::string OwnerAddress{}; + std::string OperatorAddress{}; + std::string RewardAddress{}; + uint64_t MintedBlocks{}; + uint256 StakeModifier{}; + + static std::optional TryFrom(const CBlock& block, const CBlockIndex* blockindex, const CCustomCSView& view) { + MinterInfo result; + CKeyID minter; + block.ExtractMinterKey(minter); + auto id = view.GetMasternodeIdByOperator(minter); + if (!id) return {}; + result.Id = id->ToString(); + auto mn = view.GetMasternode(*id); + if (mn) { + auto dest = mn->operatorType == 1 ? CTxDestination(PKHash(minter)) : CTxDestination(WitnessV0KeyHash(minter)); + result.OperatorAddress = EncodeDestination(dest); + } + result.MintedBlocks = blockindex->mintedBlocks; + result.StakeModifier = blockindex->stakeModifier; + return result; + } + + void ToUniValueLegacy(UniValue& result) const { + // Note: This follows legacy way of empty checks and prints to preserve + // compatibility. Don't change it. Use the new method ToUniValue method + // for new version. + if (Id.empty()) return; + result.pushKV("masternode", Id); + if (!OperatorAddress.empty()) result.pushKV("minter", OperatorAddress); + result.pushKV("mintedBlocks", MintedBlocks); + result.pushKV("stakeModifier", StakeModifier.ToString()); + } + + UniValue ToUniValue() const { + // Note that this breaks compatibility with the legacy version. + // Do not use this with existing RPCs + UniValue result(UniValue::VOBJ); + result.pushKV("id", Id); + result.pushKV("owner", OwnerAddress); + result.pushKV("operator", OperatorAddress); + result.pushKV("rewardAddress", RewardAddress); + result.pushKV("totalMinted", MintedBlocks); + result.pushKV("stakeModifier", StakeModifier.ToString()); + return result; + } +}; + +struct RewardInfo { + struct TokenRewardItems { + CAmount Burnt{}; + CAmount IncentiveFunding{}; + CAmount AnchorReward{}; + CAmount CommunityDevFunds{}; + CAmount Loan{}; + CAmount Options{}; + }; + + CAmount BlockReward{}; + TokenRewardItems TokenRewards{}; + + static std::optional TryFrom(const CBlock& block, const CBlockIndex* blockindex, const Consensus::Params& consensus) { + if (blockindex->nHeight < consensus.AMKHeight) + return {}; + + RewardInfo result{}; + auto& tokenRewards = result.TokenRewards; + CAmount blockReward = GetBlockSubsidy(blockindex->nHeight, consensus); + result.BlockReward = blockReward; + + if (blockindex->nHeight < consensus.EunosHeight) { + for (const auto& [accountType, accountVal] : consensus.blockTokenRewardsLegacy) + { + CAmount subsidy = blockReward * accountVal / COIN; + switch (accountType) { + // CommunityDevFunds, Loan, Options don't exist in blockTokenRewardsLegacy here + case CommunityAccountType::AnchorReward:{ tokenRewards.AnchorReward = subsidy; break; } + case CommunityAccountType::IncentiveFunding: { tokenRewards.IncentiveFunding = subsidy; break; } + default: { tokenRewards.Burnt += subsidy; } + } + } + return result; + } + + for (const auto& [accountType, accountVal] : consensus.blockTokenRewards) + { + if (blockindex->nHeight < consensus.GrandCentralHeight + && accountType == CommunityAccountType::CommunityDevFunds) { + continue; + } + + CAmount subsidy = CalculateCoinbaseReward(blockReward, accountVal); + switch (accountType) { + // Everything else is burnt. Should update post fort canning. Retaining + // compatibility for old logic for now. + case CommunityAccountType::AnchorReward:{ tokenRewards.AnchorReward = subsidy; break; } + case CommunityAccountType::CommunityDevFunds: { tokenRewards.CommunityDevFunds = subsidy; break; } + default: { tokenRewards.Burnt += subsidy; } + } + } + return result; + } + + void ToUniValueLegacy(UniValue& result) const { + // Note: This follows legacy way of empty checks and prints to preserve + // compatibility. Don't change it. Use the new method ToUniValue method + // for new version. + UniValue rewards(UniValue::VARR); + UniValue obj(UniValue::VOBJ); + + auto& r = TokenRewards; + auto items = std::vector>{ + { CommunityAccountType::AnchorReward, r.AnchorReward }, + { CommunityAccountType::CommunityDevFunds, r.CommunityDevFunds }, + { CommunityAccountType::Unallocated, r.Burnt }}; + + for (const auto& [accountName, val]: items) { + obj.pushKV(GetCommunityAccountName(accountName), ValueFromAmount(val)); + } + + rewards.push_back(obj); + result.pushKV("nonutxo", rewards); + } + + UniValue ToUniValue() const { + // Note that this breaks compatibility with the legacy version. + // Do not use this with existing RPCs + UniValue obj(UniValue::VOBJ); + + auto& r = TokenRewards; + auto items = std::vector>{ + { CommunityAccountType::AnchorReward, r.AnchorReward }, + { CommunityAccountType::CommunityDevFunds, r.CommunityDevFunds }, + { CommunityAccountType::IncentiveFunding, r.IncentiveFunding }, + { CommunityAccountType::Loan, r.Loan }, + { CommunityAccountType::Options, r.Options }, + { CommunityAccountType::Unallocated, r.Burnt }}; + + obj.pushKV("block", ValueFromAmount(BlockReward)); + for (const auto& [accountName, val]: items) { + obj.pushKV(GetCommunityAccountName(accountName), ValueFromAmount(val)); + } + return obj; + } +}; + +UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails, int version) { // Serialize passed information without accessing chain state of the active chain! AssertLockNotHeld(cs_main); // For performance reasons + const auto consensus = Params().GetConsensus(); + + auto txVmInfo = [](const CTransaction& tx) -> std::optional { + CustomTxType guess; + UniValue txResults(UniValue::VOBJ); + if (tx.IsCoinBase()) { + if (tx.vout.size() < 2) { + // TODO: Decode vout 0 to dvm + return {}; + } + auto tx1ScriptPubKey = tx.vout[1].scriptPubKey; + if (tx1ScriptPubKey.size() == 0) return {}; + auto res = XVM::TryFrom(tx1ScriptPubKey); + if (!res) return {}; + UniValue result(UniValue::VOBJ); + result.pushKV("vmtype", "coinbase"); + result.pushKV("txtype", "coinbase"); + result.pushKV("msg", res->ToUniValue()); + return result; + } + auto res = RpcInfo(tx, std::numeric_limits::max(), guess, txResults); + if (guess == CustomTxType::None) { + return {}; + } + UniValue result(UniValue::VOBJ); + result.pushKV("vmtype", guess == CustomTxType::EvmTx ? "evm" : "dvm"); + result.pushKV("txtype", ToString(guess)); + if (!res.ok) { + result.pushKV("error", res.msg); + } else { + result.pushKV("msg", txResults); + } + return result; + }; + + auto txsToUniValue = [&txVmInfo](const CBlock& block, bool txDetails, int version) { + UniValue txs(UniValue::VARR); + for(const auto& tx : block.vtx) + { + if (txDetails) { + UniValue objTx(UniValue::VOBJ); + TxToUniv(*tx, uint256(), objTx, version > 3, RPCSerializationFlags(), version); + if (version > 2) { + if (auto r = txVmInfo(*tx); r) { + objTx.pushKV("vm", *r); + } + } + txs.push_back(objTx); + } else { + txs.push_back(tx->GetHash().GetHex()); + } + } + return txs; + }; + + auto v3plus = version > 2; UniValue result(UniValue::VOBJ); result.pushKV("hash", blockindex->GetBlockHash().GetHex()); @@ -132,79 +336,22 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn result.pushKV("weight", (int)::GetBlockWeight(block)); result.pushKV("height", blockindex->nHeight); - CKeyID minter; - block.ExtractMinterKey(minter); - auto id = pcustomcsview->GetMasternodeIdByOperator(minter); - if (id) { - result.pushKV("masternode", id->ToString()); - auto mn = pcustomcsview->GetMasternode(*id); - if (mn) { - auto dest = mn->operatorType == 1 ? CTxDestination(PKHash(minter)) : CTxDestination(WitnessV0KeyHash(minter)); - result.pushKV("minter", EncodeDestination(dest)); - } + // For v3+, we fix the past mistakes and don't just modify existing root schema. + // We'll add all these later. + if (!v3plus) { + auto minterInfo = MinterInfo::TryFrom(block, blockindex, *pcustomcsview); + if (minterInfo) { minterInfo->ToUniValueLegacy(result); } } - result.pushKV("mintedBlocks", blockindex->mintedBlocks); - result.pushKV("stakeModifier", blockindex->stakeModifier.ToString()); - + result.pushKV("version", block.nVersion); result.pushKV("versionHex", strprintf("%08x", block.nVersion)); result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); - UniValue rewards(UniValue::VARR); - if (blockindex->nHeight >= Params().GetConsensus().AMKHeight) - { - CAmount blockReward = GetBlockSubsidy(blockindex->nHeight, Params().GetConsensus()); - UniValue nonutxo(UniValue::VOBJ); - - if (blockindex->nHeight >= Params().GetConsensus().EunosHeight) - { - CAmount burnt{0}; - for (const auto& kv : Params().GetConsensus().newNonUTXOSubsidies) - { - if (blockindex->nHeight < Params().GetConsensus().GrandCentralHeight - && kv.first == CommunityAccountType::CommunityDevFunds) { - continue; - } - CAmount subsidy = CalculateCoinbaseReward(blockReward, kv.second); - - switch(kv.first) { - case CommunityAccountType::AnchorReward: - case CommunityAccountType::CommunityDevFunds: - nonutxo.pushKV(GetCommunityAccountName(kv.first), ValueFromAmount(subsidy)); - break; - default: - burnt += subsidy; // Everything else goes into burnt - } - } - - // Add burnt total - nonutxo.pushKV(GetCommunityAccountName(CommunityAccountType::Unallocated), ValueFromAmount(burnt)); - } - else - { - for (const auto& kv : Params().GetConsensus().nonUtxoBlockSubsidies) - { - // Anchor and LP incentive - nonutxo.pushKV(GetCommunityAccountName(kv.first), ValueFromAmount(blockReward * kv.second / COIN)); - } - } - - rewards.push_back(nonutxo); + if (!v3plus) { + auto rewardInfo = RewardInfo::TryFrom(block, blockindex, consensus); + if (rewardInfo) { rewardInfo->ToUniValueLegacy(result); } + result.pushKV("tx", txsToUniValue(block, txDetails, version)); } - result.pushKV("nonutxo", rewards); - UniValue txs(UniValue::VARR); - for(const auto& tx : block.vtx) - { - if(txDetails) - { - UniValue objTx(UniValue::VOBJ); - TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags()); - txs.push_back(objTx); - } - else - txs.push_back(tx->GetHash().GetHex()); - } - result.pushKV("tx", txs); result.pushKV("time", block.GetBlockTime()); result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); result.pushKV("bits", strprintf("%08x", block.nBits)); @@ -216,6 +363,19 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex()); if (pnext) result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex()); + + if (v3plus) { + auto minterInfo = MinterInfo::TryFrom(block, blockindex, *pcustomcsview); + if (minterInfo) { + result.pushKV("minter", minterInfo->ToUniValue()); + } + auto rewardInfo = RewardInfo::TryFrom(block, blockindex, consensus); + if (rewardInfo) { + result.pushKV("rewards", rewardInfo->ToUniValue()); + } + result.pushKV("tx", txsToUniValue(block, txDetails, version)); + } + return result; } @@ -878,7 +1038,9 @@ static UniValue getblock(const JSONRPCRequest& request) RPCHelpMan{"getblock", "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n" "If verbosity is 1, returns an Object with information about block .\n" - "If verbosity is 2, returns an Object with information about block and information about each transaction. \n", + "If verbosity is 2, returns an Object with information about block and information about each transaction. \n" + "If verbosity is 3, returns an Object with version 2 API (DVM, EVM, etc). \n" + "If verbosity is 4, returns an Object with version 2 API (DVM, EVM, etc with Hex) \n", { {"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The block hash"}, {"verbosity", RPCArg::Type::NUM, /* default */ "1", "0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data"}, @@ -969,7 +1131,7 @@ static UniValue getblock(const JSONRPCRequest& request) return strHex; } - return blockToJSON(block, tip, pblockindex, verbosity >= 2); + return blockToJSON(block, tip, pblockindex, verbosity >= 2, verbosity); } static UniValue pruneblockchain(const JSONRPCRequest& request) diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 5c205a8ef95..4cc8de4d1bd 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -32,7 +32,7 @@ double GetDifficulty(const CBlockIndex* blockindex); void RPCNotifyBlockChange(bool ibd, const CBlockIndex *); /** Block description to JSON */ -UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false) LOCKS_EXCLUDED(cs_main); +UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, bool txDetails = false, int verbosity = 0) LOCKS_EXCLUDED(cs_main); /** Mempool information to JSON */ UniValue MempoolInfoToJSON(const CTxMemPool& pool); diff --git a/src/test/dip1fork_tests.cpp b/src/test/dip1fork_tests.cpp index 00cb1d2f768..ba1fed63429 100644 --- a/src/test/dip1fork_tests.cpp +++ b/src/test/dip1fork_tests.cpp @@ -70,8 +70,8 @@ BOOST_AUTO_TEST_CASE(blockreward_dfip1) CAmount const baseSubsidy = GetBlockSubsidy(height, consensus); tx.vout[1].nValue = baseSubsidy * consensus.foundationShareDFIP1 / COIN; tx.vout[0].nValue -= tx.vout[1].nValue; - tx.vout[0].nValue -= baseSubsidy * consensus.nonUtxoBlockSubsidies.at(CommunityAccountType::IncentiveFunding) / COIN; - tx.vout[0].nValue -= baseSubsidy * consensus.nonUtxoBlockSubsidies.at(CommunityAccountType::AnchorReward) / COIN; + tx.vout[0].nValue -= baseSubsidy * consensus.blockTokenRewardsLegacy.at(CommunityAccountType::IncentiveFunding) / COIN; + tx.vout[0].nValue -= baseSubsidy * consensus.blockTokenRewardsLegacy.at(CommunityAccountType::AnchorReward) / COIN; Res res = ApplyGeneralCoinbaseTx(mnview, CTransaction(tx), height, 0, consensus); BOOST_CHECK(res.ok); diff --git a/src/validation.cpp b/src/validation.cpp index 771f36eb1fa..56fb1f946d2 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2096,7 +2096,7 @@ Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int if (height >= consensus.EunosHeight) { CAmount subsidy; - for (const auto& kv : consensus.newNonUTXOSubsidies) + for (const auto& kv : consensus.blockTokenRewards) { if (kv.first == CommunityAccountType::CommunityDevFunds) { if (height < consensus.GrandCentralHeight) { @@ -2174,7 +2174,7 @@ Res ApplyGeneralCoinbaseTx(CCustomCSView & mnview, CTransaction const & tx, int } else { - for (const auto& kv : consensus.nonUtxoBlockSubsidies) { + for (const auto& kv : consensus.blockTokenRewardsLegacy) { CAmount subsidy = blockReward * kv.second / COIN; Res res = mnview.AddCommunityBalance(kv.first, subsidy); if (!res.ok) { @@ -2205,7 +2205,7 @@ void ReverseGeneralCoinbaseTx(CCustomCSView & mnview, int height, const Consensu { if (height >= Params().GetConsensus().EunosHeight) { - for (const auto& kv : Params().GetConsensus().newNonUTXOSubsidies) + for (const auto& kv : Params().GetConsensus().blockTokenRewards) { if (kv.first == CommunityAccountType::CommunityDevFunds) { if (height < Params().GetConsensus().GrandCentralHeight) { @@ -2256,7 +2256,7 @@ void ReverseGeneralCoinbaseTx(CCustomCSView & mnview, int height, const Consensu } else { - for (const auto& kv : Params().GetConsensus().nonUtxoBlockSubsidies) + for (const auto& kv : Params().GetConsensus().blockTokenRewardsLegacy) { CAmount subsidy = blockReward * kv.second / COIN; mnview.SubCommunityBalance(kv.first, subsidy);