From 90c9b9cb7c0b8af528f53acee7a15ae03a353527 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 1 Jul 2024 14:29:42 +0400 Subject: [PATCH 1/8] First step to implement spark coinbase --- src/libspark/keys.cpp | 10 ++++++++++ src/libspark/keys.h | 3 +++ src/miner.cpp | 34 +++++++++++++++++++++++++++++++++- src/spark/state.cpp | 2 ++ src/validation.cpp | 15 ++++++++------- src/wallet/wallet.cpp | 18 +++++++++++++----- src/wallet/wallet.h | 3 +++ 7 files changed, 72 insertions(+), 13 deletions(-) diff --git a/src/libspark/keys.cpp b/src/libspark/keys.cpp index 791c05a2bf..a568d539be 100644 --- a/src/libspark/keys.cpp +++ b/src/libspark/keys.cpp @@ -243,4 +243,14 @@ unsigned char Address::decode(const std::string& str) { return network; } +std::vector Address::toByteVector(const unsigned char network) const { + std::string strAddr = encode(network); + return std::vector(strAddr.begin(), strAddr.end()); +} + +unsigned char Address::fromByteVector(const std::vector& vch) { + std::string strAddr(vch.begin(), vch.end()); + return decode(strAddr); +} + } diff --git a/src/libspark/keys.h b/src/libspark/keys.h index 4af8b25687..f51213c7a0 100644 --- a/src/libspark/keys.h +++ b/src/libspark/keys.h @@ -82,6 +82,9 @@ class Address { std::string encode(const unsigned char network) const; unsigned char decode(const std::string& str); + std::vector toByteVector(const unsigned char network) const; + unsigned char fromByteVector(const std::vector& vch); + private: const Params* params; std::vector d; diff --git a/src/miner.cpp b/src/miner.cpp index 492cb3477b..558e8ed1e5 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -289,6 +289,36 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc std::vector sbPayments; FillBlockPayments(coinbaseTx, nHeight, pblock->nTime, nBlockSubsidy, pblocktemplate->voutMasternodePayments, sbPayments); + std::vector spark_outputs; + std::vector indexes; + for (size_t i = 0; i < coinbaseTx.vout.size(); ++i) { + auto& out = coinbaseTx.vout[i]; + + if (out.scriptPubKey[out.scriptPubKey.size()-1] == OP_SPARKMINT) { + spark::MintedCoinData mintedCoinData; + mintedCoinData.v = out.nValue; + std::vector vch(out.scriptPubKey.begin() + 2, out.scriptPubKey.end() - 1); + mintedCoinData.address.fromByteVector(vch); + mintedCoinData.memo = "MiningReward"; + spark_outputs.push_back(mintedCoinData); + indexes.push_back(i); + } + } + + if (!spark_outputs.empty()) { + CDataStream serialContextStream(SER_NETWORK, PROTOCOL_VERSION); + serialContextStream << 1; +// serialContextStream << pblock->GetHash(); + std::vector recipients = CSparkWallet::CreateSparkMintRecipients(spark_outputs, std::vector(serialContextStream.begin(), serialContextStream.end()), true); + + size_t i = 0; + for (size_t i = 0; i < recipients.size(); ++i) { + auto& recipient = recipients[i]; + CTxOut txout(recipient.nAmount, recipient.scriptPubKey); + coinbaseTx.vout[i] = txout; + } + } + pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vTxFees[0] = -nFees; @@ -1234,7 +1264,9 @@ void static FiroMiner(const CChainParams &chainparams) { LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", powTarget.ToString(), hashTarget.ToString()); ProcessBlockFound(pblock, chainparams); SetThreadPriority(THREAD_PRIORITY_LOWEST); - coinbaseScript->KeepScript(); + if (!GetBoolArg("-sparkreward", DEFAULT_SPARK_REWARD)) { + coinbaseScript->KeepScript(); + } // In regression test mode, stop mining after a block is found. if (chainparams.MineBlocksOnDemand()) throw boost::thread_interrupted(); diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 5359a7a9a0..fbc36ab47a 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -847,6 +847,8 @@ std::vector getSerialContext(const CTransaction &tx) { } catch (const std::exception &) { return std::vector(); } + } else if (tx.IsCoinBase()) { + serialContextStream << 1; //TODO levon } else { for (auto input: tx.vin) { input.scriptSig.clear(); diff --git a/src/validation.cpp b/src/validation.cpp index abcb55e0ff..08990324d3 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -757,13 +757,6 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe return false; } - if (tx.IsSparkTransaction()) { - if (hasExchangeUTXOs) - return state.DoS(100, false, REJECT_INVALID, "bad-exchange-address"); - if (!CheckSparkTransaction(tx, state, hashTx, isVerifyDB, nHeight, isCheckWallet, fStatefulZerocoinCheck, sparkTxInfo)) - return false; - } - const auto ¶ms = ::Params().GetConsensus(); if (tx.IsZerocoinSpend() || tx.IsZerocoinMint()) { if (!isVerifyDB && nHeight >= params.nDisableZerocoinStartBlock) @@ -777,6 +770,14 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe } } + + if (tx.IsSparkTransaction()) { //TODO levon + if (hasExchangeUTXOs) + return state.DoS(100, false, REJECT_INVALID, "bad-exchange-address"); + if (!CheckSparkTransaction(tx, state, hashTx, isVerifyDB, nHeight, isCheckWallet, fStatefulZerocoinCheck, sparkTxInfo)) + return false; + } + bool isInWhitelist = Params().GetConsensus().txidWhitelist.count(tx.GetHash()) > 0; if (nHeight >= ::Params().GetConsensus().nStartBlacklist && !isInWhitelist) { for (const auto& vin : tx.vin) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fababfae30..c75feba9c6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -6820,12 +6820,19 @@ bool CWallet::UpdatedTransaction(const uint256 &hashTx) void CWallet::GetScriptForMining(boost::shared_ptr &script) { boost::shared_ptr rKey(new CReserveKey(this)); - CPubKey pubkey; - if (!rKey->GetReservedKey(pubkey)) - return; + if (!GetBoolArg("-sparkreward", DEFAULT_SPARK_REWARD)) { //TODO levon add HF block number + CPubKey pubkey; + if (!rKey->GetReservedKey(pubkey)) + return; - script = rKey; - script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; + script = rKey; + script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; + } else { + spark::Address address = sparkWallet->getDefaultAddress(); + unsigned char network = spark::GetNetworkType(); + script = rKey; + script->reserveScript = CScript() << address.toByteVector(network) << OP_SPARKMINT; + } } void CWallet::LockCoin(const COutPoint& output) @@ -7076,6 +7083,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-zapwalletmints", _("Delete all Sigma mints and only recover those parts of the blockchain through -reindex on startup")); strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); + strUsage += HelpMessageOpt("-sparkreward", strprintf(_("Send block reward to spark address (default: %u)"), DEFAULT_SPARK_REWARD)); if (showDebug) { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index ab6e98fd7d..e5ca66a4d6 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -95,6 +95,9 @@ static const bool DEFAULT_USE_HD_WALLET = true; //! if set, all keys will be derived by using BIP39 static const bool DEFAULT_USE_MNEMONIC = true; +//! if set, block reward will be sent to your spark address +static const bool DEFAULT_SPARK_REWARD = false; + extern const char * DEFAULT_WALLET_DAT; const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; From 63f11f12040fb25f9acf2c16ac4814ee7b77a1ca Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 26 Aug 2024 12:30:01 +0400 Subject: [PATCH 2/8] Masternode Spark payouts implemented and fixes --- src/chainparams.cpp | 4 +++ src/consensus/params.h | 2 ++ src/evo/deterministicmns.cpp | 31 ++++++++++++++++++++ src/evo/deterministicmns.h | 2 ++ src/evo/providertx.cpp | 38 ++++++++++++++++++++---- src/masternode-payments.cpp | 26 +++++++++++----- src/miner.cpp | 13 ++++---- src/qt/masternodelist.cpp | 8 ++--- src/rpc/mining.cpp | 2 +- src/rpc/rpcevo.cpp | 57 +++++++++++++++++++++++++++++++----- src/spark/state.cpp | 9 +++++- src/validation.cpp | 22 +++++++++----- src/wallet/rpcwallet.cpp | 2 +- src/wallet/wallet.cpp | 17 +++++++---- 14 files changed, 186 insertions(+), 47 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index fd8a90dbbd..ab335563dd 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -403,6 +403,7 @@ class CMainParams : public CChainParams { consensus.nLelantusStartBlock = ZC_LELANTUS_STARTING_BLOCK; consensus.nLelantusFixesStartBlock = ZC_LELANTUS_FIXES_START_BLOCK; consensus.nSparkStartBlock = SPARK_START_BLOCK; + consensus.nSparkCoinbase = SPARK_START_BLOCK; //TODO levon consensus.nLelantusGracefulPeriod = LELANTUS_GRACEFUL_PERIOD; consensus.nZerocoinV2MintMempoolGracefulPeriod = ZC_V2_MINT_GRACEFUL_MEMPOOL_PERIOD; consensus.nZerocoinV2MintGracefulPeriod = ZC_V2_MINT_GRACEFUL_PERIOD; @@ -711,6 +712,7 @@ class CTestNetParams : public CChainParams { consensus.nLelantusFixesStartBlock = ZC_LELANTUS_TESTNET_FIXES_START_BLOCK; consensus.nSparkStartBlock = SPARK_TESTNET_START_BLOCK; + consensus.nSparkCoinbase = SPARK_TESTNET_START_BLOCK; //TODO levon put the real HW block consensus.nLelantusGracefulPeriod = LELANTUS_TESTNET_GRACEFUL_PERIOD; consensus.nZerocoinV2MintMempoolGracefulPeriod = ZC_V2_MINT_TESTNET_GRACEFUL_MEMPOOL_PERIOD; @@ -965,6 +967,7 @@ class CDevNetParams : public CChainParams { consensus.nLelantusFixesStartBlock = 1; consensus.nSparkStartBlock = 1500; + consensus.nSparkCoinbase = 1500; //TODO levon put the real HW block consensus.nLelantusGracefulPeriod = 6000; consensus.nMaxSigmaInputPerBlock = ZC_SIGMA_INPUT_LIMIT_PER_BLOCK; @@ -1199,6 +1202,7 @@ class CRegTestParams : public CChainParams { consensus.nLelantusStartBlock = 400; consensus.nLelantusFixesStartBlock = 400; consensus.nSparkStartBlock = 1000; + consensus.nSparkCoinbase = 1300; consensus.nExchangeAddressStartBlock = 1000; consensus.nLelantusGracefulPeriod = 1500; consensus.nZerocoinV2MintMempoolGracefulPeriod = 1; diff --git a/src/consensus/params.h b/src/consensus/params.h index 1db0a6ac3c..3d313d0de1 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -253,6 +253,8 @@ struct Params { int nSparkStartBlock; + int nSparkCoinbase; + int nLelantusGracefulPeriod; // Lelantus Blacklist diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index c9d9e519b3..36b4ba1237 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -23,6 +23,19 @@ static const std::string DB_LIST_DIFF = "dmn_D"; CDeterministicMNManager* deterministicMNManager; +std::string ToStringSparkAddress(const CScript script) { + std::vector vch(script.begin() + 2, script.end() - 1); + try { + const spark::Params* params = spark::Params::get_default(); + spark::Address sPayoutAddress(params); + sPayoutAddress.fromByteVector(vch); + // if we passed this point, this means it is spark address, just make it string, + return std::string(vch.begin(), vch.end()); + } catch (const std::exception &) { + } + return std::string(); +} + std::string CDeterministicMNState::ToString() const { CTxDestination dest; @@ -30,9 +43,18 @@ std::string CDeterministicMNState::ToString() const std::string operatorPayoutAddress = "none"; if (ExtractDestination(scriptPayout, dest)) { payoutAddress = CBitcoinAddress(dest).ToString(); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + if (!strScriptPayout.empty()) + payoutAddress = strScriptPayout; } + if (ExtractDestination(scriptOperatorPayout, dest)) { operatorPayoutAddress = CBitcoinAddress(dest).ToString(); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptOperatorPayout); + if (!strScriptPayout.empty()) + operatorPayoutAddress = strScriptPayout; } return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, " @@ -59,11 +81,20 @@ void CDeterministicMNState::ToJson(UniValue& obj) const if (ExtractDestination(scriptPayout, dest)) { CBitcoinAddress payoutAddress(dest); obj.push_back(Pair("payoutAddress", payoutAddress.ToString())); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + if (!strScriptPayout.empty()) + obj.push_back(Pair("payoutAddress", strScriptPayout)); } + obj.push_back(Pair("pubKeyOperator", pubKeyOperator.Get().ToString())); if (ExtractDestination(scriptOperatorPayout, dest)) { CBitcoinAddress operatorPayoutAddress(dest); obj.push_back(Pair("operatorPayoutAddress", operatorPayoutAddress.ToString())); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptOperatorPayout); + if (!strScriptPayout.empty()) + obj.push_back(Pair("operatorPayoutAddress", strScriptPayout)); } } diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 767169e063..94d2bdfc88 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -27,6 +27,8 @@ namespace llmq class CFinalCommitment; } +std::string ToStringSparkAddress(const CScript script); + class CDeterministicMNState { public: diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 02dc664e70..5bd11eca14 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -107,12 +107,12 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid if (ptx.keyIDOwner.IsNull() || !ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); } - if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { + if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { //TODO levon return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); } CTxDestination payoutDest; - if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { + if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { //TODO levon // should not happen as we checked script types before return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest"); } @@ -248,7 +248,7 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa // don't allow to set operator reward payee in case no operatorReward was set return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); } - if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) { + if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) { //TODO levon return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); } } @@ -286,12 +286,12 @@ bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVal if (!ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); } - if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { + if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { //TODO levon return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); } CTxDestination payoutDest; - if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { + if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { //TODO levon // should not happen as we checked script types before return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest"); } @@ -392,7 +392,7 @@ std::string CProRegTx::MakeSignString() const CTxDestination destPayout; CBitcoinAddress addrPayout; std::string strPayout; - if (ExtractDestination(scriptPayout, destPayout) && addrPayout.Set(destPayout)) { + if (ExtractDestination(scriptPayout, destPayout) && addrPayout.Set(destPayout)) { //TODO levon strPayout = addrPayout.ToString(); } else { strPayout = HexStr(scriptPayout.begin(), scriptPayout.end()); @@ -415,6 +415,10 @@ std::string CProRegTx::ToString() const std::string payee = "unknown"; if (ExtractDestination(scriptPayout, dest)) { payee = CBitcoinAddress(dest).ToString(); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + if (!strScriptPayout.empty()) + payee = strScriptPayout; } return strprintf("CProRegTx(nVersion=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s)", @@ -436,7 +440,13 @@ void CProRegTx::ToJson(UniValue& obj) const if (ExtractDestination(scriptPayout, dest)) { CBitcoinAddress bitcoinAddress(dest); obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString())); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + if (!strScriptPayout.empty()) + obj.push_back(Pair("payoutAddress", strScriptPayout)); } + + obj.push_back(Pair("pubKeyOperator", pubKeyOperator.ToString())); obj.push_back(Pair("operatorReward", (double)nOperatorReward / 100)); @@ -449,6 +459,10 @@ std::string CProUpServTx::ToString() const std::string payee = "unknown"; if (ExtractDestination(scriptOperatorPayout, dest)) { payee = CBitcoinAddress(dest).ToString(); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptOperatorPayout); + if (!strScriptPayout.empty()) + payee = strScriptPayout; } return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, addr=%s, operatorPayoutAddress=%s)", @@ -466,6 +480,10 @@ void CProUpServTx::ToJson(UniValue& obj) const if (ExtractDestination(scriptOperatorPayout, dest)) { CBitcoinAddress bitcoinAddress(dest); obj.push_back(Pair("operatorPayoutAddress", bitcoinAddress.ToString())); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptOperatorPayout); + if (!strScriptPayout.empty()) + obj.push_back(Pair("operatorPayoutAddress", strScriptPayout)); } obj.push_back(Pair("inputsHash", inputsHash.ToString())); } @@ -476,6 +494,10 @@ std::string CProUpRegTx::ToString() const std::string payee = "unknown"; if (ExtractDestination(scriptPayout, dest)) { payee = CBitcoinAddress(dest).ToString(); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + if (!strScriptPayout.empty()) + payee = strScriptPayout; } return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s)", @@ -493,6 +515,10 @@ void CProUpRegTx::ToJson(UniValue& obj) const if (ExtractDestination(scriptPayout, dest)) { CBitcoinAddress bitcoinAddress(dest); obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString())); + } else { + std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + if (!strScriptPayout.empty()) + obj.push_back(Pair("payoutAddress", strScriptPayout)); } obj.push_back(Pair("pubKeyOperator", pubKeyOperator.ToString())); obj.push_back(Pair("inputsHash", inputsHash.ToString())); diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 907a303072..f10b9f21e3 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -137,9 +137,15 @@ std::string GetRequiredPaymentsString(int nBlockHeight, const CDeterministicMNCP std::string strPayee = "Unknown"; if (payee) { CTxDestination dest; - if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) + if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) { + std::string strScriptPayout = ToStringSparkAddress(payee->pdmnState->scriptPayout); + if (!strScriptPayout.empty()) + strPayee = strScriptPayout; assert(false); - strPayee = CBitcoinAddress(dest).ToString(); + } else { + strPayee = CBitcoinAddress(dest).ToString(); + } + } /* if (CSuperblockManager::IsSuperblockTriggered(nBlockHeight)) { @@ -200,10 +206,16 @@ bool CMasternodePayments::GetMasternodeTxOuts(int nBlockHeight, int nTime, CAmou for (const auto& txout : voutMasternodePaymentsRet) { CTxDestination address1; - ExtractDestination(txout.scriptPubKey, address1); - CBitcoinAddress address2(address1); - - LogPrintf("CMasternodePayments::%s -- Znode payment %lld to %s\n", __func__, txout.nValue, address2.ToString()); + std::string strAddr; + if (ExtractDestination(txout.scriptPubKey, address1)) { + CBitcoinAddress address2(address1); + strAddr = address2.ToString(); + } else { + std::string strScriptPayout = ToStringSparkAddress(txout.scriptPubKey); + if (!strScriptPayout.empty()) + strAddr = strScriptPayout; + } + LogPrintf("CMasternodePayments::%s -- Znode payment %lld to %s\n", __func__, txout.nValue, strAddr); } return true; @@ -275,7 +287,7 @@ bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlo for (const auto& txout : voutMasternodePayments) { bool found = false; - for (const auto& txout2 : txNew.vout) { + for (const auto& txout2 : txNew.vout) { //TODO levon cover spark case if (txout == txout2) { found = true; break; diff --git a/src/miner.cpp b/src/miner.cpp index 558e8ed1e5..13e94ae4f6 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -298,8 +298,12 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc spark::MintedCoinData mintedCoinData; mintedCoinData.v = out.nValue; std::vector vch(out.scriptPubKey.begin() + 2, out.scriptPubKey.end() - 1); - mintedCoinData.address.fromByteVector(vch); - mintedCoinData.memo = "MiningReward"; + try { + mintedCoinData.address.fromByteVector(vch); + } catch (const std::exception &) { + throw std::runtime_error(strprintf("Invalid Spark address")); + } + mintedCoinData.memo = "BlockReward"; spark_outputs.push_back(mintedCoinData); indexes.push_back(i); } @@ -307,15 +311,14 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc if (!spark_outputs.empty()) { CDataStream serialContextStream(SER_NETWORK, PROTOCOL_VERSION); - serialContextStream << 1; -// serialContextStream << pblock->GetHash(); + serialContextStream << pindexPrev->GetBlockHash(); std::vector recipients = CSparkWallet::CreateSparkMintRecipients(spark_outputs, std::vector(serialContextStream.begin(), serialContextStream.end()), true); size_t i = 0; for (size_t i = 0; i < recipients.size(); ++i) { auto& recipient = recipients[i]; CTxOut txout(recipient.nAmount, recipient.scriptPubKey); - coinbaseTx.vout[i] = txout; + coinbaseTx.vout[indexes[i]] = txout; } } diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp index a5b5e399e0..fc9fed6b25 100644 --- a/src/qt/masternodelist.cpp +++ b/src/qt/masternodelist.cpp @@ -206,8 +206,8 @@ void MasternodeList::updateDIP3List() if (walletModel && ui->checkBoxMyMasternodesOnly->isChecked()) { bool fMyMasternode = setOutpts.count(dmn->collateralOutpoint) || walletModel->IsSpendable(dmn->pdmnState->keyIDOwner) || - walletModel->IsSpendable(dmn->pdmnState->scriptPayout) || - walletModel->IsSpendable(dmn->pdmnState->scriptOperatorPayout); + walletModel->IsSpendable(dmn->pdmnState->scriptPayout) || //TODO levon + walletModel->IsSpendable(dmn->pdmnState->scriptOperatorPayout); //TODO levon if (!fMyMasternode) return; } // populate list @@ -221,7 +221,7 @@ void MasternodeList::updateDIP3List() CTxDestination payeeDest; QString payeeStr = tr("UNKNOWN"); - if (ExtractDestination(dmn->pdmnState->scriptPayout, payeeDest)) { + if (ExtractDestination(dmn->pdmnState->scriptPayout, payeeDest)) { //TODO levon payeeStr = QString::fromStdString(CBitcoinAddress(payeeDest).ToString()); } QTableWidgetItem* payeeItem = new QTableWidgetItem(payeeStr); @@ -232,7 +232,7 @@ void MasternodeList::updateDIP3List() if (dmn->pdmnState->scriptOperatorPayout != CScript()) { CTxDestination operatorDest; - if (ExtractDestination(dmn->pdmnState->scriptOperatorPayout, operatorDest)) { + if (ExtractDestination(dmn->pdmnState->scriptOperatorPayout, operatorDest)) { //TODO levon operatorRewardStr += tr("to %1").arg(QString::fromStdString(CBitcoinAddress(operatorDest).ToString())); } else { operatorRewardStr += tr("to UNKNOWN"); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 637a079cfb..1c7492ab0e 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -835,7 +835,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) for (const auto& txout : pblocktemplate->voutMasternodePayments) { CTxDestination address1; ExtractDestination(txout.scriptPubKey, address1); - CBitcoinAddress address2(address1); + CBitcoinAddress address2(address1); //TODO levon check spark case UniValue obj(UniValue::VOBJ); obj.push_back(Pair("payee", address2.ToString().c_str())); diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 6037b4835f..cd378e9648 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -159,6 +159,24 @@ static CBLSSecretKey ParseBLSSecretKey(const std::string& hexKey, const std::str #ifdef ENABLE_WALLET +spark::Address parseSparkAddress(const std::string strAddr) { + const spark::Params* params = spark::Params::get_default(); + spark::Address sPayoutAddress(params); + unsigned char network = spark::GetNetworkType(); + unsigned char coinNetwork = sPayoutAddress.decode(strAddr); + if (coinNetwork != network) + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid spark address, wrong network type")); + int nTxHeight; + { + LOCK(cs_main); + nTxHeight = chainActive.Height(); + } + if (nTxHeight > ::Params().GetConsensus().nSparkCoinbase) + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Spark Coinbase is not active")); + + return sPayoutAddress; +} + template static void FundSpecialTx(CWallet* pwallet, CMutableTransaction& tx, const SpecialTxPayload& payload, const CTxDestination& fundDest) { @@ -470,21 +488,38 @@ UniValue protx_register(const JSONRPCRequest& request) } ptx.nOperatorReward = operatorReward; + bool isSparkAddress = false; + const spark::Params* params = spark::Params::get_default(); + spark::Address sPayoutAddress(params); + try { + parseSparkAddress(request.params[paramIdx + 5].get_str()); + isSparkAddress = true; + } catch (const std::invalid_argument &) { + //contiune + } CBitcoinAddress payoutAddress(request.params[paramIdx + 5].get_str()); - if (!payoutAddress.IsValid()) { + if (!isSparkAddress && !payoutAddress.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[paramIdx + 5].get_str())); } ptx.keyIDOwner = keyOwner.GetPubKey().GetID(); ptx.pubKeyOperator = pubKeyOperator; ptx.keyIDVoting = keyIDVoting; - ptx.scriptPayout = GetScriptForDestination(payoutAddress.Get()); + if (isSparkAddress) { + unsigned char network = spark::GetNetworkType(); + ptx.scriptPayout = CScript() << sPayoutAddress.toByteVector(network) << OP_SPARKMINT; + } else { + ptx.scriptPayout = GetScriptForDestination(payoutAddress.Get()); + } if (!isFundRegister) { // make sure fee calculation works ptx.vchSig.resize(65); } + if (isSparkAddress && request.params.size() <= paramIdx + 6) + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("You should provide a separate change address, in case you give spark payout address")); + CBitcoinAddress fundAddress = payoutAddress; if (request.params.size() > paramIdx + 6) { fundAddress = CBitcoinAddress(request.params[paramIdx + 6].get_str()); @@ -641,7 +676,7 @@ UniValue protx_update_service(const JSONRPCRequest& request) if (!payoutAddress.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[4].get_str())); } - ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get()); + ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get()); //TODO levon } } else { ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; @@ -658,10 +693,10 @@ UniValue protx_update_service(const JSONRPCRequest& request) } else { if (ptx.scriptOperatorPayout != CScript()) { // use operator reward address as default source for fees - ExtractDestination(ptx.scriptOperatorPayout, feeSource); + ExtractDestination(ptx.scriptOperatorPayout, feeSource); //TODO levon } else { // use payout address as default source for fees - ExtractDestination(dmn->pdmnState->scriptPayout, feeSource); + ExtractDestination(dmn->pdmnState->scriptPayout, feeSource); //TODO levon } } @@ -729,7 +764,7 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) if (!payoutAddress.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[4].get_str())); } - ptx.scriptPayout = GetScriptForDestination(payoutAddress.Get()); + ptx.scriptPayout = GetScriptForDestination(payoutAddress.Get()); //TODO levon CKey keyOwner; if (!pwallet->GetKey(dmn->pdmnState->keyIDOwner, keyOwner)) { @@ -825,12 +860,12 @@ UniValue protx_revoke(const JSONRPCRequest& request) } else if (dmn->pdmnState->scriptOperatorPayout != CScript()) { // Using funds from previousely specified operator payout address CTxDestination txDest; - ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); + ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); //TODO levon FundSpecialTx(pwallet, tx, ptx, txDest); } else if (dmn->pdmnState->scriptPayout != CScript()) { // Using funds from previousely specified znode payout address CTxDestination txDest; - ExtractDestination(dmn->pdmnState->scriptPayout, txDest); + ExtractDestination(dmn->pdmnState->scriptPayout, txDest); //TODO levon FundSpecialTx(pwallet, tx, ptx, txDest); } else { throw JSONRPCError(RPC_INTERNAL_ERROR, "No payout or fee source addresses found, can't revoke"); @@ -887,6 +922,12 @@ static bool CheckWalletOwnsScript(CWallet* pwallet, const CScript& script) { return true; } } + // check spark case + if (!pwallet->sparkWallet) { + return false; + } + if (pwallet->sparkWallet->isAddressMine(std::string(script.begin() + 2, script.end() - 1))) + return true; return false; #endif } diff --git a/src/spark/state.cpp b/src/spark/state.cpp index fbc36ab47a..8d16992730 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -848,7 +848,14 @@ std::vector getSerialContext(const CTransaction &tx) { return std::vector(); } } else if (tx.IsCoinBase()) { - serialContextStream << 1; //TODO levon + std::vector coins = GetSparkMintCoins(tx); + if (coins.empty()) + return std::vector(); + + int height = sparkState.GetMintedCoinHeightAndId(coins[0]).first; + // get the previous block + CBlockIndex *mintBlock = chainActive[height - 1]; + serialContextStream << *mintBlock->phashBlock; } else { for (auto input: tx.vin) { input.scriptSig.clear(); diff --git a/src/validation.cpp b/src/validation.cpp index 08990324d3..4699867b73 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -733,6 +733,13 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe return state.DoS(100, false, REJECT_INVALID, "bad-cb-length"); if (hasExchangeUTXOs) return state.DoS(100, false, REJECT_INVALID, "bad-exchange-address"); + + if (tx.IsSparkTransaction()) { + if (nTxHeight < ::Params().GetConsensus().nSparkCoinbase) + return state.DoS(100, false, REJECT_INVALID, "bad-spark-coinbase"); + if (!CheckSparkTransaction(tx, state, hashTx, isVerifyDB, nHeight, isCheckWallet, fStatefulZerocoinCheck, sparkTxInfo)) + return false; + } } else { @@ -757,6 +764,13 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe return false; } + if (tx.IsSparkTransaction()) { + if (hasExchangeUTXOs) + return state.DoS(100, false, REJECT_INVALID, "bad-exchange-address"); + if (!CheckSparkTransaction(tx, state, hashTx, isVerifyDB, nHeight, isCheckWallet, fStatefulZerocoinCheck, sparkTxInfo)) + return false; + } + const auto ¶ms = ::Params().GetConsensus(); if (tx.IsZerocoinSpend() || tx.IsZerocoinMint()) { if (!isVerifyDB && nHeight >= params.nDisableZerocoinStartBlock) @@ -770,14 +784,6 @@ bool CheckTransaction(const CTransaction &tx, CValidationState &state, bool fChe } } - - if (tx.IsSparkTransaction()) { //TODO levon - if (hasExchangeUTXOs) - return state.DoS(100, false, REJECT_INVALID, "bad-exchange-address"); - if (!CheckSparkTransaction(tx, state, hashTx, isVerifyDB, nHeight, isCheckWallet, fStatefulZerocoinCheck, sparkTxInfo)) - return false; - } - bool isInWhitelist = Params().GetConsensus().txidWhitelist.count(tx.GetHash()) > 0; if (nHeight >= ::Params().GetConsensus().nStartBlacklist && !isInWhitelist) { for (const auto& vin : tx.vin) { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index a5ade6b12f..acef1b12af 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1733,7 +1733,7 @@ void ListTransactions(CWallet * const pwallet, const CWalletTx& wtx, const std:: for(CTxOut const & out : voutMasternodePaymentsRet) { CTxDestination payeeDest; ExtractDestination(out.scriptPubKey, payeeDest); - CBitcoinAddress payeeAddr(payeeDest); + CBitcoinAddress payeeAddr(payeeDest); //TODO levon check spark case if(addr.ToString() == payeeAddr.ToString()) { its_znode_payment = true; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c75feba9c6..cd0ea3774a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -6820,18 +6820,23 @@ bool CWallet::UpdatedTransaction(const uint256 &hashTx) void CWallet::GetScriptForMining(boost::shared_ptr &script) { boost::shared_ptr rKey(new CReserveKey(this)); - if (!GetBoolArg("-sparkreward", DEFAULT_SPARK_REWARD)) { //TODO levon add HF block number + int nTxHeight; + { + LOCK(cs_main); + nTxHeight = chainActive.Height(); + } + if (GetBoolArg("-sparkreward", DEFAULT_SPARK_REWARD) && nTxHeight > ::Params().GetConsensus().nSparkCoinbase) { + spark::Address address = sparkWallet->getDefaultAddress(); + unsigned char network = spark::GetNetworkType(); + script = rKey; + script->reserveScript = CScript() << address.toByteVector(network) << OP_SPARKMINT; + } else{ CPubKey pubkey; if (!rKey->GetReservedKey(pubkey)) return; script = rKey; script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; - } else { - spark::Address address = sparkWallet->getDefaultAddress(); - unsigned char network = spark::GetNetworkType(); - script = rKey; - script->reserveScript = CScript() << address.toByteVector(network) << OP_SPARKMINT; } } From 0ae0b8004e838cc49aa3e01015554aa02a465554 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 23 Sep 2024 13:32:16 +0400 Subject: [PATCH 3/8] More fixes and cleanup --- src/evo/deterministicmns.cpp | 21 +++----------- src/evo/deterministicmns.h | 2 -- src/evo/providertx.cpp | 24 ++++++++-------- src/libspark/coin.cpp | 14 ++++++++-- src/libspark/coin.h | 15 ++++++++-- src/masternode-payments.cpp | 49 ++++++++++++++++++++++++++++---- src/miner.cpp | 2 +- src/qt/masternodelist.cpp | 18 +++++++++--- src/qt/walletmodel.cpp | 3 ++ src/rpc/mining.cpp | 17 ++++++++++-- src/rpc/rpcevo.cpp | 54 ++++++++++++++++++++++++++++++------ src/spark/state.cpp | 36 ++++++++++++++++++++++++ src/spark/state.h | 4 +++ src/wallet/rpcwallet.cpp | 2 +- 14 files changed, 201 insertions(+), 60 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 36b4ba1237..59b387096d 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -23,19 +23,6 @@ static const std::string DB_LIST_DIFF = "dmn_D"; CDeterministicMNManager* deterministicMNManager; -std::string ToStringSparkAddress(const CScript script) { - std::vector vch(script.begin() + 2, script.end() - 1); - try { - const spark::Params* params = spark::Params::get_default(); - spark::Address sPayoutAddress(params); - sPayoutAddress.fromByteVector(vch); - // if we passed this point, this means it is spark address, just make it string, - return std::string(vch.begin(), vch.end()); - } catch (const std::exception &) { - } - return std::string(); -} - std::string CDeterministicMNState::ToString() const { CTxDestination dest; @@ -44,7 +31,7 @@ std::string CDeterministicMNState::ToString() const if (ExtractDestination(scriptPayout, dest)) { payoutAddress = CBitcoinAddress(dest).ToString(); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptPayout); if (!strScriptPayout.empty()) payoutAddress = strScriptPayout; } @@ -52,7 +39,7 @@ std::string CDeterministicMNState::ToString() const if (ExtractDestination(scriptOperatorPayout, dest)) { operatorPayoutAddress = CBitcoinAddress(dest).ToString(); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptOperatorPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptOperatorPayout); if (!strScriptPayout.empty()) operatorPayoutAddress = strScriptPayout; } @@ -82,7 +69,7 @@ void CDeterministicMNState::ToJson(UniValue& obj) const CBitcoinAddress payoutAddress(dest); obj.push_back(Pair("payoutAddress", payoutAddress.ToString())); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptPayout); if (!strScriptPayout.empty()) obj.push_back(Pair("payoutAddress", strScriptPayout)); } @@ -92,7 +79,7 @@ void CDeterministicMNState::ToJson(UniValue& obj) const CBitcoinAddress operatorPayoutAddress(dest); obj.push_back(Pair("operatorPayoutAddress", operatorPayoutAddress.ToString())); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptOperatorPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptOperatorPayout); if (!strScriptPayout.empty()) obj.push_back(Pair("operatorPayoutAddress", strScriptPayout)); } diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 94d2bdfc88..767169e063 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -27,8 +27,6 @@ namespace llmq class CFinalCommitment; } -std::string ToStringSparkAddress(const CScript script); - class CDeterministicMNState { public: diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 5bd11eca14..705561ddb0 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -107,12 +107,12 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid if (ptx.keyIDOwner.IsNull() || !ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); } - if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { //TODO levon + if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash() && !spark::IsPayToSparkAddress(ptx.scriptPayout)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); } CTxDestination payoutDest; - if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { //TODO levon + if (!ExtractDestination(ptx.scriptPayout, payoutDest) && !spark::IsPayToSparkAddress(ptx.scriptPayout)) { // should not happen as we checked script types before return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest"); } @@ -248,7 +248,7 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa // don't allow to set operator reward payee in case no operatorReward was set return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); } - if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) { //TODO levon + if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash() && !spark::IsPayToSparkAddress(ptx.scriptOperatorPayout)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-payee"); } } @@ -286,12 +286,12 @@ bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVal if (!ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); } - if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { //TODO levon + if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash() && !spark::IsPayToSparkAddress(ptx.scriptPayout)) { return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee"); } CTxDestination payoutDest; - if (!ExtractDestination(ptx.scriptPayout, payoutDest)) { //TODO levon + if (!ExtractDestination(ptx.scriptPayout, payoutDest) && !spark::IsPayToSparkAddress(ptx.scriptPayout)) { // should not happen as we checked script types before return state.DoS(10, false, REJECT_INVALID, "bad-protx-payee-dest"); } @@ -392,7 +392,7 @@ std::string CProRegTx::MakeSignString() const CTxDestination destPayout; CBitcoinAddress addrPayout; std::string strPayout; - if (ExtractDestination(scriptPayout, destPayout) && addrPayout.Set(destPayout)) { //TODO levon + if (ExtractDestination(scriptPayout, destPayout) && addrPayout.Set(destPayout)) { strPayout = addrPayout.ToString(); } else { strPayout = HexStr(scriptPayout.begin(), scriptPayout.end()); @@ -416,7 +416,7 @@ std::string CProRegTx::ToString() const if (ExtractDestination(scriptPayout, dest)) { payee = CBitcoinAddress(dest).ToString(); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptPayout); if (!strScriptPayout.empty()) payee = strScriptPayout; } @@ -441,7 +441,7 @@ void CProRegTx::ToJson(UniValue& obj) const CBitcoinAddress bitcoinAddress(dest); obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString())); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptPayout); if (!strScriptPayout.empty()) obj.push_back(Pair("payoutAddress", strScriptPayout)); } @@ -460,7 +460,7 @@ std::string CProUpServTx::ToString() const if (ExtractDestination(scriptOperatorPayout, dest)) { payee = CBitcoinAddress(dest).ToString(); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptOperatorPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptOperatorPayout); if (!strScriptPayout.empty()) payee = strScriptPayout; } @@ -481,7 +481,7 @@ void CProUpServTx::ToJson(UniValue& obj) const CBitcoinAddress bitcoinAddress(dest); obj.push_back(Pair("operatorPayoutAddress", bitcoinAddress.ToString())); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptOperatorPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptOperatorPayout); if (!strScriptPayout.empty()) obj.push_back(Pair("operatorPayoutAddress", strScriptPayout)); } @@ -495,7 +495,7 @@ std::string CProUpRegTx::ToString() const if (ExtractDestination(scriptPayout, dest)) { payee = CBitcoinAddress(dest).ToString(); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptPayout); if (!strScriptPayout.empty()) payee = strScriptPayout; } @@ -516,7 +516,7 @@ void CProUpRegTx::ToJson(UniValue& obj) const CBitcoinAddress bitcoinAddress(dest); obj.push_back(Pair("payoutAddress", bitcoinAddress.ToString())); } else { - std::string strScriptPayout = ToStringSparkAddress(scriptPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(scriptPayout); if (!strScriptPayout.empty()) obj.push_back(Pair("payoutAddress", strScriptPayout)); } diff --git a/src/libspark/coin.cpp b/src/libspark/coin.cpp index b7d87fd89b..a0be0d558c 100644 --- a/src/libspark/coin.cpp +++ b/src/libspark/coin.cpp @@ -25,7 +25,7 @@ Coin::Coin( this->serial_context = serial_context; // Validate the type - if (type != COIN_TYPE_MINT && type != COIN_TYPE_SPEND) { + if (type != COIN_TYPE_MINT && type != COIN_TYPE_SPEND && type != COIN_TYPE_COINBASE) { throw std::invalid_argument("Bad coin type"); } this->type = type; @@ -60,7 +60,7 @@ Coin::Coin( // - if (this->type == COIN_TYPE_MINT) { + if (this->type == COIN_TYPE_MINT || this->type == COIN_TYPE_COINBASE) { this->v = v; // Encrypt recipient data MintCoinRecipientData r; @@ -81,6 +81,9 @@ Coin::Coin( r_stream << r; this->r_ = AEAD::encrypt(address.get_Q1()*SparkUtils::hash_k(k), "Spend coin data", r_stream); } + + if (this->type == COIN_TYPE_COINBASE) + this->k = k; } // Validate a coin for identification @@ -154,7 +157,7 @@ IdentifiedCoinData Coin::identify(const IncomingViewKey& incoming_view_key) { } catch (const std::exception &) { throw std::runtime_error("Unable to identify coin"); } - + // Check that the memo length is valid unsigned char memo_length = r.padded_memo[0]; if (memo_length > this->params->get_memo_bytes()) { @@ -221,4 +224,9 @@ void Coin::setParams(const Params* params) { this->params = params; } +bool Coin::isValidMNPayment(const spark::Address& addr, const std::vector& serialContext) const { + Coin c(this->params, COIN_TYPE_MINT, k, addr, v, "BlockReward", serial_context); + return this->getHash() == c.getHash(); +} + } diff --git a/src/libspark/coin.h b/src/libspark/coin.h index 664d522643..8a40f81685 100644 --- a/src/libspark/coin.h +++ b/src/libspark/coin.h @@ -15,6 +15,7 @@ using namespace secp_primitives; // Flags for coin types: those generated from mints, and those generated from spends const char COIN_TYPE_MINT = 0; const char COIN_TYPE_SPEND = 1; +const char COIN_TYPE_COINBASE = 2; struct IdentifiedCoinData { uint64_t i; // diversifier @@ -93,6 +94,10 @@ class Coin { void setParams(const Params* params); void setSerialContext(const std::vector& serial_context_); + + // this is used ONLY to check masternode payout address validity, + bool isValidMNPayment(const spark::Address& addr, const std::vector& serialContext) const; + protected: bool validate(const IncomingViewKey& incoming_view_key, IdentifiedCoinData& data); @@ -102,6 +107,7 @@ class Coin { GroupElement S, K, C; // serial commitment, recovery key, value commitment AEADEncryptedData r_; // encrypted recipient data uint64_t v; // value + Scalar k; // nonce, is serialized only for coinbase o std::vector serial_context; // context to which the serial commitment should be bound (not serialized, but inferred) // Serialization depends on the coin type @@ -110,7 +116,7 @@ class Coin { inline void SerializationOp(Stream& s, Operation ser_action) { // The type must be valid READWRITE(type); - if (type != COIN_TYPE_MINT && type != COIN_TYPE_SPEND) { + if (type != COIN_TYPE_MINT && type != COIN_TYPE_SPEND && type != COIN_TYPE_COINBASE) { throw std::invalid_argument("Cannot deserialize coin due to bad type"); } READWRITE(S); @@ -125,7 +131,7 @@ class Coin { if (ser_action.ForRead()) { this->params = spark::Params::get_default(); } - if (type == COIN_TYPE_MINT && r_.ciphertext.size() != (1 + AES_BLOCKSIZE) + SCALAR_ENCODING + (1 + params->get_memo_bytes() + 1)) { + if ((type == COIN_TYPE_MINT || COIN_TYPE_COINBASE) && r_.ciphertext.size() != (1 + AES_BLOCKSIZE) + SCALAR_ENCODING + (1 + params->get_memo_bytes() + 1)) { throw std::invalid_argument("Cannot deserialize mint coin due to bad encrypted data"); } if (type == COIN_TYPE_SPEND && r_.ciphertext.size() != 8 + (1 + AES_BLOCKSIZE) + SCALAR_ENCODING + (1 + params->get_memo_bytes() + 1)) { @@ -135,6 +141,11 @@ class Coin { if (type == COIN_TYPE_MINT) { READWRITE(v); } + + if (type == COIN_TYPE_COINBASE) { + READWRITE(v); + READWRITE(k); + } } }; diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index f10b9f21e3..42a85fca9f 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -138,7 +138,7 @@ std::string GetRequiredPaymentsString(int nBlockHeight, const CDeterministicMNCP if (payee) { CTxDestination dest; if (!ExtractDestination(payee->pdmnState->scriptPayout, dest)) { - std::string strScriptPayout = ToStringSparkAddress(payee->pdmnState->scriptPayout); + std::string strScriptPayout = spark::ToStringSparkAddress(payee->pdmnState->scriptPayout); if (!strScriptPayout.empty()) strPayee = strScriptPayout; assert(false); @@ -211,7 +211,7 @@ bool CMasternodePayments::GetMasternodeTxOuts(int nBlockHeight, int nTime, CAmou CBitcoinAddress address2(address1); strAddr = address2.ToString(); } else { - std::string strScriptPayout = ToStringSparkAddress(txout.scriptPubKey); + std::string strScriptPayout = spark::ToStringSparkAddress(txout.scriptPubKey); if (!strScriptPayout.empty()) strAddr = strScriptPayout; } @@ -285,12 +285,49 @@ bool CMasternodePayments::IsTransactionValid(const CTransaction& txNew, int nBlo return true; } + std::vector scripts; + for (const auto& txout : txNew.vout) { + if (!txout.scriptPubKey.empty() && txout.scriptPubKey.IsSparkMint()) { + scripts.push_back(txout.scriptPubKey); + } + } + const spark::Params* params = spark::Params::get_default(); + std::vector coins; + std::vector serialContext; + if (!scripts.empty()) { + spark::MintTransaction mintTransaction(params); + try { + spark::ParseSparkMintTransaction(scripts, mintTransaction); + } catch (std::invalid_argument&) { + LogPrintf("CMasternodePayments::%s -- ERROR failed to parse mint outputs in block at height %s\n", __func__, nBlockHeight); + return false; + } + + //checking whether MintTransaction is valid + if (!mintTransaction.verify()) { + LogPrintf("CMasternodePayments::%s -- ERROR failed to verify mints in block at height %s\n", __func__, nBlockHeight); + return false; + } + mintTransaction.getCoins(coins); + serialContext = spark::getSerialContext(txNew); + } + for (const auto& txout : voutMasternodePayments) { bool found = false; - for (const auto& txout2 : txNew.vout) { //TODO levon cover spark case - if (txout == txout2) { - found = true; - break; + spark::Address addr(params); + if (spark::IsPayToSparkAddress(txout.scriptPubKey, addr) && !coins.empty()) { + for (const auto &coin: coins) { + if (coin.isValidMNPayment(addr, serialContext)) { + found = true; + break; + } + } + } else { + for (const auto &txout2: txNew.vout) { + if (txout == txout2) { + found = true; + break; + } } } if (!found) { diff --git a/src/miner.cpp b/src/miner.cpp index 13e94ae4f6..afe5fd0a63 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -294,7 +294,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc for (size_t i = 0; i < coinbaseTx.vout.size(); ++i) { auto& out = coinbaseTx.vout[i]; - if (out.scriptPubKey[out.scriptPubKey.size()-1] == OP_SPARKMINT) { + if (spark::IsPayToSparkAddress(out.scriptPubKey)) { spark::MintedCoinData mintedCoinData; mintedCoinData.v = out.nValue; std::vector vch(out.scriptPubKey.begin() + 2, out.scriptPubKey.end() - 1); diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp index fc9fed6b25..2b1d45439a 100644 --- a/src/qt/masternodelist.cpp +++ b/src/qt/masternodelist.cpp @@ -206,8 +206,8 @@ void MasternodeList::updateDIP3List() if (walletModel && ui->checkBoxMyMasternodesOnly->isChecked()) { bool fMyMasternode = setOutpts.count(dmn->collateralOutpoint) || walletModel->IsSpendable(dmn->pdmnState->keyIDOwner) || - walletModel->IsSpendable(dmn->pdmnState->scriptPayout) || //TODO levon - walletModel->IsSpendable(dmn->pdmnState->scriptOperatorPayout); //TODO levon + walletModel->IsSpendable(dmn->pdmnState->scriptPayout) || + walletModel->IsSpendable(dmn->pdmnState->scriptOperatorPayout); if (!fMyMasternode) return; } // populate list @@ -219,10 +219,16 @@ void MasternodeList::updateDIP3List() QTableWidgetItem* lastPaidItem = new QTableWidgetItem((dmn->pdmnState->nLastPaidHeight < params.DIP0003EnforcementHeight) ? tr("NONE") : QString::number(dmn->pdmnState->nLastPaidHeight)); QTableWidgetItem* nextPaymentItem = new QTableWidgetItem(nextPayments.count(dmn->proTxHash) ? QString::number(nextPayments[dmn->proTxHash]) : tr("UNKNOWN")); + const spark::Params* params = spark::Params::get_default(); + unsigned char network = spark::GetNetworkType(); + CTxDestination payeeDest; + spark::Address addr(params); QString payeeStr = tr("UNKNOWN"); - if (ExtractDestination(dmn->pdmnState->scriptPayout, payeeDest)) { //TODO levon + if (ExtractDestination(dmn->pdmnState->scriptPayout, payeeDest)) { payeeStr = QString::fromStdString(CBitcoinAddress(payeeDest).ToString()); + } else if (spark::IsPayToSparkAddress(dmn->pdmnState->scriptPayout, addr)) { + payeeStr = QString::fromStdString(addr.encode(network)); } QTableWidgetItem* payeeItem = new QTableWidgetItem(payeeStr); @@ -232,8 +238,12 @@ void MasternodeList::updateDIP3List() if (dmn->pdmnState->scriptOperatorPayout != CScript()) { CTxDestination operatorDest; - if (ExtractDestination(dmn->pdmnState->scriptOperatorPayout, operatorDest)) { //TODO levon + spark::Address operatorAddr(params); + std::string strScriptPayout = spark::ToStringSparkAddress(dmn->pdmnState->scriptOperatorPayout); + if (ExtractDestination(dmn->pdmnState->scriptOperatorPayout, operatorDest)) { operatorRewardStr += tr("to %1").arg(QString::fromStdString(CBitcoinAddress(operatorDest).ToString())); + } else if (spark::IsPayToSparkAddress(dmn->pdmnState->scriptOperatorPayout, operatorAddr)) { + operatorRewardStr += tr("to %1").arg(QString::fromStdString(operatorAddr.encode(network))); } else { operatorRewardStr += tr("to UNKNOWN"); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index e6908f973d..d4d8b34919 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -1002,6 +1002,9 @@ bool WalletModel::IsSpendable(const CTxDestination& dest) const bool WalletModel::IsSpendable(const CScript& script) const { + std::string sparkAddr = spark::ToStringSparkAddress(script); + if (wallet->sparkWallet && wallet->sparkWallet->isAddressMine(sparkAddr)) + return true; return IsMine(*wallet, script) & ISMINE_SPENDABLE; } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 1c7492ab0e..561a8decc5 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -831,14 +831,25 @@ UniValue getblocktemplate(const JSONRPCRequest& request) std::vector voutMasternodePayments; mnpayments.GetBlockTxOuts(chainActive.Height() + 1, pblock->nTime, 0, voutMasternodePayments); + const spark::Params* params = spark::Params::get_default(); + unsigned char network = spark::GetNetworkType(); + UniValue masternodeObj(UniValue::VARR); for (const auto& txout : pblocktemplate->voutMasternodePayments) { + std::string strAddr; CTxDestination address1; - ExtractDestination(txout.scriptPubKey, address1); - CBitcoinAddress address2(address1); //TODO levon check spark case + spark::Address sparkAddr(params); + + if (spark::IsPayToSparkAddress(txout.scriptPubKey, sparkAddr)) { + strAddr = sparkAddr.encode(network); + } else { + ExtractDestination(txout.scriptPubKey, address1); + CBitcoinAddress address2(address1); + strAddr = address2.ToString(); + } UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("payee", address2.ToString().c_str())); + obj.push_back(Pair("payee", strAddr.c_str())); obj.push_back(Pair("script", HexStr(txout.scriptPubKey))); obj.push_back(Pair("amount", txout.nValue)); masternodeObj.push_back(obj); diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index cd378e9648..0bb1a9e441 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -667,16 +667,31 @@ UniValue protx_update_service(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; + bool isSparkAddress = false; // param operatorPayoutAddress if (request.params.size() >= 5) { if (request.params[4].get_str().empty()) { ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; } else { + bool isSparkAddress = false; + const spark::Params* params = spark::Params::get_default(); + spark::Address sPayoutAddress(params); + try { + parseSparkAddress(request.params[4].get_str()); + isSparkAddress = true; + } catch (const std::invalid_argument &) { + //contiune + } CBitcoinAddress payoutAddress(request.params[4].get_str()); - if (!payoutAddress.IsValid()) { + if (!isSparkAddress && !payoutAddress.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[4].get_str())); } - ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get()); //TODO levon + if (isSparkAddress) { + unsigned char network = spark::GetNetworkType(); + ptx.scriptOperatorPayout = CScript() << sPayoutAddress.toByteVector(network) << OP_SPARKMINT; + } else { + ptx.scriptOperatorPayout = GetScriptForDestination(payoutAddress.Get()); + } } } else { ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; @@ -693,10 +708,12 @@ UniValue protx_update_service(const JSONRPCRequest& request) } else { if (ptx.scriptOperatorPayout != CScript()) { // use operator reward address as default source for fees - ExtractDestination(ptx.scriptOperatorPayout, feeSource); //TODO levon + if (!ExtractDestination(ptx.scriptOperatorPayout, feeSource)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Please add separate fee address, in case your Operator payout address is spark")); } else { // use payout address as default source for fees - ExtractDestination(dmn->pdmnState->scriptPayout, feeSource); //TODO levon + if (!ExtractDestination(dmn->pdmnState->scriptPayout, feeSource)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Please add separate fee address, in case your payout address is spark")); } } @@ -760,11 +777,27 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) ptx.keyIDVoting = ParsePubKeyIDFromAddress(request.params[3].get_str(), "voting address"); } + bool isSparkAddress = false; + const spark::Params* params = spark::Params::get_default(); + spark::Address sPayoutAddress(params); + try { + parseSparkAddress(request.params[4].get_str()); + isSparkAddress = true; + } catch (const std::invalid_argument &) { + //contiune + } + CBitcoinAddress payoutAddress(request.params[4].get_str()); - if (!payoutAddress.IsValid()) { + if (!isSparkAddress && !payoutAddress.IsValid()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[4].get_str())); } - ptx.scriptPayout = GetScriptForDestination(payoutAddress.Get()); //TODO levon + + if (isSparkAddress) { + unsigned char network = spark::GetNetworkType(); + ptx.scriptPayout = CScript() << sPayoutAddress.toByteVector(network) << OP_SPARKMINT; + } else { + ptx.scriptPayout = GetScriptForDestination(payoutAddress.Get()); + } CKey keyOwner; if (!pwallet->GetKey(dmn->pdmnState->keyIDOwner, keyOwner)) { @@ -852,6 +885,10 @@ UniValue protx_revoke(const JSONRPCRequest& request) tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_UPDATE_REVOKE; + if (request.params.size() <= 4 && !spark::IsPayToSparkAddress(dmn->pdmnState->scriptOperatorPayout) && !spark::IsPayToSparkAddress(dmn->pdmnState->scriptPayout)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("You need to provide fee source address, payout and operator payout addresses are spark.")); + } + if (request.params.size() > 4) { CBitcoinAddress feeSourceAddress = CBitcoinAddress(request.params[4].get_str()); if (!feeSourceAddress.IsValid()) @@ -860,12 +897,11 @@ UniValue protx_revoke(const JSONRPCRequest& request) } else if (dmn->pdmnState->scriptOperatorPayout != CScript()) { // Using funds from previousely specified operator payout address CTxDestination txDest; - ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); //TODO levon - FundSpecialTx(pwallet, tx, ptx, txDest); + ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); } else if (dmn->pdmnState->scriptPayout != CScript()) { // Using funds from previousely specified znode payout address CTxDestination txDest; - ExtractDestination(dmn->pdmnState->scriptPayout, txDest); //TODO levon + ExtractDestination(dmn->pdmnState->scriptPayout, txDest); FundSpecialTx(pwallet, tx, ptx, txDest); } else { throw JSONRPCError(RPC_INTERNAL_ERROR, "No payout or fee source addresses found, can't revoke"); diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 8d16992730..790661edcb 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -78,6 +78,42 @@ unsigned char GetNetworkType() { return ADDRESS_NETWORK_REGTEST; } +bool IsPayToSparkAddress(const CScript& script) +{ + const spark::Params* params = spark::Params::get_default(); + spark::Address addr(params); + return IsPayToSparkAddress(script, addr); +} + +bool IsPayToSparkAddress(const CScript& script, spark::Address& addr) +{ if (script[script.size()-1] != OP_SPARKMINT) + return false; + unsigned char network = spark::GetNetworkType(); + unsigned char coinNetwork; + + std::vector vch(script.begin() + 2, script.end() - 1); + + try { + coinNetwork = addr.fromByteVector(vch); + } catch (...) { + return false; + } + return network == coinNetwork; +} + +std::string ToStringSparkAddress(const CScript script) { + std::vector vch(script.begin() + 2, script.end() - 1); + try { + const spark::Params* params = spark::Params::get_default(); + spark::Address sPayoutAddress(params); + sPayoutAddress.fromByteVector(vch); + // if we passed this point, this means it is spark address, just make it string, + return std::string(vch.begin(), vch.end()); + } catch (const std::exception &) { + } + return std::string(); +} + /* * Util funtions */ diff --git a/src/spark/state.h b/src/spark/state.h index 3820cb1a1d..9e2b43fd38 100644 --- a/src/spark/state.h +++ b/src/spark/state.h @@ -40,6 +40,10 @@ class CSparkTxInfo { bool IsSparkAllowed(); bool IsSparkAllowed(int height); unsigned char GetNetworkType(); +bool IsPayToSparkAddress(const CScript& script); +bool IsPayToSparkAddress(const CScript& script, spark::Address& addr); +std::string ToStringSparkAddress(const CScript script); + // Pass Scripts form mint transaction and get spark MintTransaction object void ParseSparkMintTransaction(const std::vector& scripts, MintTransaction& mintTransaction); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index acef1b12af..a5ade6b12f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1733,7 +1733,7 @@ void ListTransactions(CWallet * const pwallet, const CWalletTx& wtx, const std:: for(CTxOut const & out : voutMasternodePaymentsRet) { CTxDestination payeeDest; ExtractDestination(out.scriptPubKey, payeeDest); - CBitcoinAddress payeeAddr(payeeDest); //TODO levon check spark case + CBitcoinAddress payeeAddr(payeeDest); if(addr.ToString() == payeeAddr.ToString()) { its_znode_payment = true; From dc29f5bd990c764cdd22ca3f98c1528d02956494 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 7 Oct 2024 11:22:46 +0400 Subject: [PATCH 4/8] Minor fixes --- src/chainparams.cpp | 2 +- src/libspark/coin.h | 2 +- src/spark/state.cpp | 21 +++++++++++++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 98c0b4a22d..2d8ca13e15 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -1222,7 +1222,7 @@ class CRegTestParams : public CChainParams { consensus.nLelantusStartBlock = 400; consensus.nLelantusFixesStartBlock = 400; consensus.nSparkStartBlock = 1000; - consensus.nSparkCoinbase = 1300; + consensus.nSparkCoinbase = consensus.nSparkStartBlock; consensus.nExchangeAddressStartBlock = 1000; consensus.nLelantusGracefulPeriod = 1500; consensus.nZerocoinV2MintMempoolGracefulPeriod = 1; diff --git a/src/libspark/coin.h b/src/libspark/coin.h index 8a40f81685..8eb3439e3a 100644 --- a/src/libspark/coin.h +++ b/src/libspark/coin.h @@ -131,7 +131,7 @@ class Coin { if (ser_action.ForRead()) { this->params = spark::Params::get_default(); } - if ((type == COIN_TYPE_MINT || COIN_TYPE_COINBASE) && r_.ciphertext.size() != (1 + AES_BLOCKSIZE) + SCALAR_ENCODING + (1 + params->get_memo_bytes() + 1)) { + if ((type == COIN_TYPE_MINT || type == COIN_TYPE_COINBASE) && r_.ciphertext.size() != (1 + AES_BLOCKSIZE) + SCALAR_ENCODING + (1 + params->get_memo_bytes() + 1)) { throw std::invalid_argument("Cannot deserialize mint coin due to bad encrypted data"); } if (type == COIN_TYPE_SPEND && r_.ciphertext.size() != 8 + (1 + AES_BLOCKSIZE) + SCALAR_ENCODING + (1 + params->get_memo_bytes() + 1)) { diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 409bb09ea5..46f3fdd4b0 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -86,7 +86,7 @@ bool IsPayToSparkAddress(const CScript& script) } bool IsPayToSparkAddress(const CScript& script, spark::Address& addr) -{ if (script[script.size()-1] != OP_SPARKMINT) +{ if (script.empty() || script[script.size()-1] != OP_SPARKMINT) return false; unsigned char network = spark::GetNetworkType(); unsigned char coinNetwork; @@ -102,6 +102,9 @@ bool IsPayToSparkAddress(const CScript& script, spark::Address& addr) } std::string ToStringSparkAddress(const CScript script) { + if (script.empty()) + return ""; + std::vector vch(script.begin() + 2, script.end() - 1); try { const spark::Params* params = spark::Params::get_default(); @@ -893,7 +896,21 @@ std::vector getSerialContext(const CTransaction &tx) { return std::vector(); } } else if (tx.IsCoinBase()) { - std::vector coins = GetSparkMintCoins(tx); + std::vector coins; + + for (const auto& vout : tx.vout) { + const auto& script = vout.scriptPubKey; + if (script.IsSparkMint()) { + try { + spark::Coin coin(Params::get_default()); + ParseSparkMintCoin(script, coin); + coins.push_back(coin); + } catch (const std::exception &) { + //Continue + } + } + } + if (coins.empty()) return std::vector(); From 05d5c849a6ea489a36e6afa63aef6da0ec27e20f Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 11 Nov 2024 08:40:02 +0400 Subject: [PATCH 5/8] Make spark coinbase imature untill 100blocks passed --- src/chain.h | 14 ++++++++-- src/libspark/coin.cpp | 4 +-- src/libspark/mint_transaction.cpp | 2 +- src/libspark/mint_transaction.h | 1 + src/miner.cpp | 1 + src/spark/sparkwallet.cpp | 35 +++++++++++++++++++----- src/spark/sparkwallet.h | 6 +++-- src/spark/state.cpp | 44 +++++++++++++++++++------------ src/spark/state.h | 4 +-- src/test/spark_state_test.cpp | 18 ++++++------- src/test/spark_tests.cpp | 6 ++++- src/test/test_bitcoin.cpp | 2 +- src/validation.cpp | 5 +++- src/wallet/wallet.cpp | 10 ++++--- 14 files changed, 104 insertions(+), 48 deletions(-) diff --git a/src/chain.h b/src/chain.h index a62f14cb41..ef5304c02e 100644 --- a/src/chain.h +++ b/src/chain.h @@ -251,7 +251,7 @@ class CBlockIndex //! Map id to std::map> anonymitySetHash; //! Map id to spark coin - std::map> sparkMintedCoins; + std::map>> sparkMintedCoins; //! Map id to std::map> sparkSetHash; //! map spark coin S to tx hash, this is used when you run with -mobile @@ -560,7 +560,17 @@ class CDiskBlockIndex : public CBlockIndex if (!(s.GetType() & SER_GETHASH) && nHeight >= params.nSparkStartBlock) { - READWRITE(sparkMintedCoins); + if (nHeight >=params.nSparkCoinbase) { + READWRITE(sparkMintedCoins); + } else { + std::map> sparkCoins; + READWRITE(sparkCoins); + for (auto& itr : sparkCoins) { + sparkMintedCoins[itr.first].reserve(itr.second.size()); + for (auto& mint : itr.second) + sparkMintedCoins[itr.first].emplace_back(std::make_pair(mint, false)); + } + } READWRITE(sparkSetHash); READWRITE(spentLTags); diff --git a/src/libspark/coin.cpp b/src/libspark/coin.cpp index a0be0d558c..8ea1a96908 100644 --- a/src/libspark/coin.cpp +++ b/src/libspark/coin.cpp @@ -126,7 +126,7 @@ IdentifiedCoinData Coin::identify(const IncomingViewKey& incoming_view_key) { IdentifiedCoinData data; // Deserialization means this process depends on the coin type - if (this->type == COIN_TYPE_MINT) { + if (this->type == COIN_TYPE_MINT || this->type == COIN_TYPE_COINBASE) { MintCoinRecipientData r; try { @@ -225,7 +225,7 @@ void Coin::setParams(const Params* params) { } bool Coin::isValidMNPayment(const spark::Address& addr, const std::vector& serialContext) const { - Coin c(this->params, COIN_TYPE_MINT, k, addr, v, "BlockReward", serial_context); + Coin c(this->params, COIN_TYPE_COINBASE, k, addr, v, "BlockReward", serial_context); return this->getHash() == c.getHash(); } diff --git a/src/libspark/mint_transaction.cpp b/src/libspark/mint_transaction.cpp index f52a3b094e..79ba6bdd9d 100644 --- a/src/libspark/mint_transaction.cpp +++ b/src/libspark/mint_transaction.cpp @@ -30,7 +30,7 @@ MintTransaction::MintTransaction( k.randomize(); this->coins.emplace_back(Coin( this->params, - COIN_TYPE_MINT, + output.type, k, output.address, output.v, diff --git a/src/libspark/mint_transaction.h b/src/libspark/mint_transaction.h index 4a41b00f42..fe4fc108c3 100644 --- a/src/libspark/mint_transaction.h +++ b/src/libspark/mint_transaction.h @@ -13,6 +13,7 @@ struct MintedCoinData { Address address; uint64_t v; std::string memo; + char type; }; class MintTransaction { diff --git a/src/miner.cpp b/src/miner.cpp index 558c0bf4d9..bc01240661 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -297,6 +297,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc if (spark::IsPayToSparkAddress(out.scriptPubKey)) { spark::MintedCoinData mintedCoinData; mintedCoinData.v = out.nValue; + mintedCoinData.type = spark::COIN_TYPE_COINBASE; std::vector vch(out.scriptPubKey.begin() + 2, out.scriptPubKey.end() - 1); try { mintedCoinData.address.fromByteVector(vch); diff --git a/src/spark/sparkwallet.cpp b/src/spark/sparkwallet.cpp index 382cb6c62d..1ce8106888 100644 --- a/src/spark/sparkwallet.cpp +++ b/src/spark/sparkwallet.cpp @@ -13,7 +13,7 @@ const uint32_t DEFAULT_SPARK_NCOUNT = 1; -CSparkWallet::CSparkWallet(const std::string& strWalletFile) { +CSparkWallet::CSparkWallet(const std::string& strWalletFile, const uint32_t& height) { CWalletDB walletdb(strWalletFile); this->strWalletFile = strWalletFile; @@ -77,6 +77,8 @@ CSparkWallet::CSparkWallet(const std::string& strWalletFile) { if (fWalletJustUnlocked) pwalletMain->Lock(); + + this->height = height; } CSparkWallet::~CSparkWallet() { @@ -108,6 +110,9 @@ CAmount CSparkWallet::getAvailableBalance() { if (mint.nHeight < 1) continue; + if (mint.type == spark::COIN_TYPE_COINBASE && (height - mint.nHeight) < COINBASE_MATURITY) + continue; + result += mint.v; } @@ -123,7 +128,10 @@ CAmount CSparkWallet::getUnconfirmedBalance() { continue; // Continue if confirmed - if (mint.nHeight > 1) + if (mint.nHeight > 1 && mint.type != spark::COIN_TYPE_COINBASE) + continue; + + if (mint.type == spark::COIN_TYPE_COINBASE && (height - mint.nHeight) > COINBASE_MATURITY) continue; result += mint.v; @@ -152,6 +160,9 @@ CAmount CSparkWallet::getAddressAvailableBalance(const spark::Address& address) if (address.get_d() != mint.d) continue; + if (mint.type == spark::COIN_TYPE_COINBASE && (height - mint.nHeight) < COINBASE_MATURITY) + continue; + result += mint.v; } @@ -175,6 +186,9 @@ CAmount CSparkWallet::getAddressUnconfirmedBalance(const spark::Address& address continue; result += mint.v; + + if (mint.type == spark::COIN_TYPE_COINBASE && (height - mint.nHeight) > COINBASE_MATURITY) + continue; } return result; @@ -297,7 +311,7 @@ std::vector CSparkWallet::ListSparkMints(bool fUnusedOnly, bool continue; // Not confirmed - if (fMatureOnly && mint.nHeight < 1) + if (fMatureOnly && (mint.nHeight < 1 || (mint.type == spark::COIN_TYPE_COINBASE && (height - mint.nHeight) < COINBASE_MATURITY))) continue; setMints.push_back(mint); @@ -488,6 +502,7 @@ void CSparkWallet::UpdateSpendStateFromMempool(const std::vector& } void CSparkWallet::UpdateSpendStateFromBlock(const CBlock& block) { + height = block.nHeight; const auto& transactions = block.vtx; ((ParallelOpThreadPool*)threadPool)->PostTask([=]() { LOCK(cs_spark_wallet); @@ -647,11 +662,11 @@ void CSparkWallet::UpdateMintStateFromBlock(const CBlock& block) { }); } -void CSparkWallet::RemoveSparkMints(const std::vector& mints) { +void CSparkWallet::RemoveSparkMints(const std::vector>& mints) { for (auto coin : mints) { try { - spark::IdentifiedCoinData identifiedCoinData = coin.identify(this->viewKey); - spark::RecoveredCoinData recoveredCoinData = coin.recover(this->fullViewKey, identifiedCoinData); + spark::IdentifiedCoinData identifiedCoinData = coin.first.identify(this->viewKey); + spark::RecoveredCoinData recoveredCoinData = coin.first.recover(this->fullViewKey, identifiedCoinData); CWalletDB walletdb(strWalletFile); uint256 lTagHash = primitives::GetLTagHash(recoveredCoinData.T); @@ -679,7 +694,11 @@ void CSparkWallet::RemoveSparkSpends(const std::unordered_map } void CSparkWallet::AbandonSparkMints(const std::vector& mints) { - RemoveSparkMints(mints); + std::vector> mints_; + mints_.reserve(mints.size()); + for (auto& mint : mints) + mints_.emplace_back(std::make_pair(mint, false)); + RemoveSparkMints(mints_); } void CSparkWallet::AbandonSpends(const std::vector& spends) { @@ -850,6 +869,7 @@ bool CSparkWallet::CreateSparkMintTransactions( if (autoMintAll) { spark::MintedCoinData mintedCoinData; mintedCoinData.v = mintedValue; + mintedCoinData.type = spark::COIN_TYPE_MINT; mintedCoinData.memo = ""; mintedCoinData.address = getDefaultAddress(); singleTxOutputs.push_back(mintedCoinData); @@ -860,6 +880,7 @@ bool CSparkWallet::CreateSparkMintTransactions( uint64_t singleMintValue = std::min(remainingMintValue, remainingOutputs.begin()->v); spark::MintedCoinData mintedCoinData; mintedCoinData.v = singleMintValue; + mintedCoinData.type = spark::COIN_TYPE_MINT; mintedCoinData.address = remainingOutputs.begin()->address; mintedCoinData.memo = remainingOutputs.begin()->memo; singleTxOutputs.push_back(mintedCoinData); diff --git a/src/spark/sparkwallet.h b/src/spark/sparkwallet.h index 8f707a5f94..bb3d0b3bf8 100644 --- a/src/spark/sparkwallet.h +++ b/src/spark/sparkwallet.h @@ -22,7 +22,7 @@ const uint32_t SPARK_CHANGE_D = 0x270F; class CSparkWallet { public: - CSparkWallet(const std::string& strWalletFile); + CSparkWallet(const std::string& strWalletFile, const uint32_t& height); ~CSparkWallet(); // increment diversifier and generate address for that spark::Address generateNextAddress(); @@ -98,7 +98,7 @@ class CSparkWallet { void UpdateMintState(const std::vector& coins, const uint256& txHash, CWalletDB& walletdb); void UpdateMintStateFromMempool(const std::vector& coins, const uint256& txHash); void UpdateMintStateFromBlock(const CBlock& block); - void RemoveSparkMints(const std::vector& mints); + void RemoveSparkMints(const std::vector>& mints); void RemoveSparkSpends(const std::unordered_map& spends); void AbandonSparkMints(const std::vector& mints); void AbandonSpends(const std::vector& spends); @@ -163,6 +163,8 @@ class CSparkWallet { std::unordered_map coinMeta; void* threadPool; + + uint32_t height; }; diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 46f3fdd4b0..3b4eb3326f 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -1,6 +1,7 @@ #include "state.h" #include "../validation.h" #include "../batchproof_container.h" +#include "../consensus/consensus.h" namespace spark { @@ -438,7 +439,8 @@ bool CheckSparkMintTransaction( CValidationState &state, uint256 hashTx, bool fStatefulSigmaCheck, - CSparkTxInfo* sparkTxInfo) { + CSparkTxInfo* sparkTxInfo, + bool isCoinbase) { LogPrintf("CheckSparkMintTransaction txHash = %s\n", hashTx.GetHex()); const spark::Params* params = spark::Params::get_default(); @@ -490,7 +492,7 @@ bool CheckSparkMintTransaction( if (sparkTxInfo != NULL && !sparkTxInfo->fInfoIsComplete) { // Update coin list in the info - sparkTxInfo->mints.push_back(coin); + sparkTxInfo->mints.push_back(std::make_pair(coin, isCoinbase)); sparkTxInfo->spTransactions.insert(hashTx); } } @@ -526,7 +528,7 @@ bool CheckSparkSMintTransaction( for (auto& coin : out_coins) { if (sparkTxInfo != NULL && !sparkTxInfo->fInfoIsComplete) { // Update coin list in the info - sparkTxInfo->mints.push_back(coin); + sparkTxInfo->mints.push_back(std::make_pair(coin, false)); } } @@ -641,6 +643,7 @@ bool CheckSparkSpendTransaction( // find index for block with hash of accumulatorBlockHash or set index to the coinGroup.firstBlock if not found while (index != coinGroup.firstBlock && index->GetBlockHash() != idAndHash.second) index = index->pprev; + CBlockIndex *lastBlock = index; // take the hash from last block of anonymity set std::vector set_hash = GetAnonymitySetHash(index, idAndHash.first); @@ -664,8 +667,11 @@ bool CheckSparkSpendTransaction( const auto& coin, index->sparkMintedCoins[id]) { set_size++; - if (!useBatching) - cover_set.push_back(coin); + if (!useBatching) { + if ((coin.second && lastBlock->nHeight > COINBASE_MATURITY) || !coin.second) { + cover_set.push_back(coin.first); + } + } } } } @@ -783,7 +789,7 @@ bool CheckSparkTransaction( } if (!txOuts.empty()) { try { - if (!CheckSparkMintTransaction(txOuts, state, hashTx, fStatefulSigmaCheck, sparkTxInfo)) { + if (!CheckSparkMintTransaction(txOuts, state, hashTx, fStatefulSigmaCheck, sparkTxInfo, tx.IsCoinBase())) { LogPrintf("CheckSparkTransaction::Mint verification failed.\n"); return false; } @@ -1055,7 +1061,7 @@ void CSparkState::AddMintsToStateAndBlockIndex( CBlockIndex *index, const CBlock* pblock) { - std::vector blockMints = pblock->sparkTxInfo->mints; + std::vector> blockMints = pblock->sparkTxInfo->mints; latestCoinId = std::max(1, latestCoinId); auto &coinGroup = coinGroups[latestCoinId]; @@ -1086,18 +1092,18 @@ void CSparkState::AddMintsToStateAndBlockIndex( } for (const auto& mint : blockMints) { - AddMint(mint, CMintedCoinInfo::make(latestCoinId, index->nHeight)); + AddMint(mint.first, CMintedCoinInfo::make(latestCoinId, index->nHeight)); LogPrintf("AddMintsToStateAndBlockIndex: Spark mint added id=%d\n", latestCoinId); index->sparkMintedCoins[latestCoinId].push_back(mint); if (GetBoolArg("-mobile", false)) { COutPoint outPoint; - GetOutPointFromBlock(outPoint, mint, *pblock); + GetOutPointFromBlock(outPoint, mint.first, *pblock); CTransactionRef tx; for (CTransactionRef itr : pblock->vtx) { if (outPoint.hash == itr->GetHash()) tx = itr; } - index->sparkTxHashContext[mint.S] = {outPoint.hash, getSerialContext(*tx)}; + index->sparkTxHashContext[mint.first.S] = {outPoint.hash, getSerialContext(*tx)}; } } } @@ -1142,7 +1148,7 @@ void CSparkState::AddBlock(CBlockIndex *index) { latestCoinId = coins.first; for (auto const &coin : coins.second) { - AddMint(coin, CMintedCoinInfo::make(coins.first, index->nHeight)); + AddMint(coin.first, CMintedCoinInfo::make(coins.first, index->nHeight)); } } @@ -1208,7 +1214,7 @@ void CSparkState::RemoveBlock(CBlockIndex *index) { // roll back mints for (auto const&coins : index->sparkMintedCoins) { for (auto const& coin : coins.second) { - auto mintCoins = GetMints().equal_range(coin); + auto mintCoins = GetMints().equal_range(coin.first); auto coinIt = find_if( mintCoins.first, mintCoins.second, [&coins](const std::unordered_map::value_type& v) { @@ -1327,7 +1333,9 @@ int CSparkState::GetCoinSetForSpend( numberOfCoins += block->sparkMintedCoins[id].size(); if (block->sparkMintedCoins.count(id) > 0) { for (const auto &coin : block->sparkMintedCoins[id]) { - coins_out.push_back(coin); + if ((coin.second && coinGroup.lastBlock->nHeight > COINBASE_MATURITY) || !coin.second) { + coins_out.push_back(coin.first); + } } } } @@ -1379,10 +1387,12 @@ void CSparkState::GetCoinsForRecovery( numberOfCoins += block->sparkMintedCoins[id].size(); if (block->sparkMintedCoins.count(id) > 0) { for (const auto &coin : block->sparkMintedCoins[id]) { - std::pair> txHashContext; - if (block->sparkTxHashContext.count(coin.S)) - txHashContext = block->sparkTxHashContext[coin.S]; - coins.push_back({coin, txHashContext}); + if ((coin.second && coinGroup.lastBlock->nHeight > COINBASE_MATURITY) || !coin.second) { + std::pair> txHashContext; + if (block->sparkTxHashContext.count(coin.first.S)) + txHashContext = block->sparkTxHashContext[coin.first.S]; + coins.push_back({coin.first, txHashContext}); + } } } } diff --git a/src/spark/state.h b/src/spark/state.h index f1abfebb6b..5ed35620c3 100644 --- a/src/spark/state.h +++ b/src/spark/state.h @@ -21,8 +21,8 @@ class CSparkTxInfo { // all the spark transactions encountered so far std::set spTransactions; - // Vector of all mints - std::vector mints; + // Vector of all mints, paired with bool,indicating if it is coinbase or not + std::vector> mints; // linking tag for every spend (map from lTag to coin group id) std::unordered_map spentLTags; diff --git a/src/test/spark_state_test.cpp b/src/test/spark_state_test.cpp index 01d63727d2..24dd2fb54f 100644 --- a/src/test/spark_state_test.cpp +++ b/src/test/spark_state_test.cpp @@ -54,7 +54,7 @@ class SparkStateTests : public SparkTestingSetup void PopulateSparkTxInfo( CBlock& block, - std::vector const& mints, + std::vector> const& mints, std::vector > const& lTags) { block.sparkTxInfo = std::make_shared(); @@ -85,13 +85,13 @@ BOOST_AUTO_TEST_CASE(add_mints_to_state) mempool.clear(); auto blockIdx1 = GenerateBlock({txs[0]}); auto block1 = GetCBlock(blockIdx1); - PopulateSparkTxInfo(block1, {pwalletMain->sparkWallet->getCoinFromMeta(mints[0])}, {}); + PopulateSparkTxInfo(block1, {{pwalletMain->sparkWallet->getCoinFromMeta(mints[0]), false}}, {}); sparkState->AddMintsToStateAndBlockIndex(blockIdx1, &block1); auto blockIdx2 = GenerateBlock({txs[1]}); auto block2 = GetCBlock(blockIdx2); - PopulateSparkTxInfo(block2, {pwalletMain->sparkWallet->getCoinFromMeta(mints[1])}, {}); + PopulateSparkTxInfo(block2, {{pwalletMain->sparkWallet->getCoinFromMeta(mints[1]), false}}, {}); sparkState->AddMintsToStateAndBlockIndex(blockIdx2, &block2); @@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE(lTag_adding) auto blockIdx = chainActive.Tip(); auto block = GetCBlock(blockIdx); - PopulateSparkTxInfo(block, {{pwalletMain->sparkWallet->getCoinFromMeta(mints[0])}}, {}); + PopulateSparkTxInfo(block, {{pwalletMain->sparkWallet->getCoinFromMeta(mints[0]), false}}, {}); sparkState->AddMintsToStateAndBlockIndex(blockIdx, &block); @@ -176,7 +176,7 @@ BOOST_AUTO_TEST_CASE(mempool) auto blockIdx = chainActive.Tip(); auto block = GetCBlock(blockIdx); - PopulateSparkTxInfo(block, {{pwalletMain->sparkWallet->getCoinFromMeta(mint)}}, {}); + PopulateSparkTxInfo(block, {{pwalletMain->sparkWallet->getCoinFromMeta(mint), false}}, {}); sparkState->AddMintsToStateAndBlockIndex(blockIdx, &block); @@ -267,7 +267,7 @@ BOOST_AUTO_TEST_CASE(add_remove_block) auto index2 = GenerateBlock({}); auto block2 = GetCBlock(index2); - PopulateSparkTxInfo(block2, {pwalletMain->sparkWallet->getCoinFromMeta(mint1), pwalletMain->sparkWallet->getCoinFromMeta(mint2)}, {}); + PopulateSparkTxInfo(block2, {{pwalletMain->sparkWallet->getCoinFromMeta(mint1), false}, {pwalletMain->sparkWallet->getCoinFromMeta(mint2), false}}, {}); sparkState->AddMintsToStateAndBlockIndex(index2, &block2); sparkState->AddBlock(index2); @@ -298,7 +298,7 @@ BOOST_AUTO_TEST_CASE(add_remove_block) auto index4 = GenerateBlock({}); auto block4 = GetCBlock(index4); - PopulateSparkTxInfo(block4, {pwalletMain->sparkWallet->getCoinFromMeta(mint3)}, {{lTag3, 1}}); + PopulateSparkTxInfo(block4, {{pwalletMain->sparkWallet->getCoinFromMeta(mint3), false}}, {{lTag3, 1}}); sparkState->AddMintsToStateAndBlockIndex(index4, &block4); index4->spentLTags = block4.sparkTxInfo->spentLTags; @@ -348,8 +348,8 @@ BOOST_AUTO_TEST_CASE(get_coin_group) PopulateSparkTxInfo( block, { - pwalletMain->sparkWallet->getCoinFromMeta(mints[i]), - pwalletMain->sparkWallet->getCoinFromMeta(mints[i + 1]) + {pwalletMain->sparkWallet->getCoinFromMeta(mints[i]), false}, + {pwalletMain->sparkWallet->getCoinFromMeta(mints[i + 1]), false} }, {}); diff --git a/src/test/spark_tests.cpp b/src/test/spark_tests.cpp index ae4962a327..85dbd1901a 100644 --- a/src/test/spark_tests.cpp +++ b/src/test/spark_tests.cpp @@ -493,7 +493,11 @@ BOOST_AUTO_TEST_CASE(checktransaction) txs[0], state, tx.GetHash(), false, chainActive.Height(), true, true, &info)); std::vector expectedCoins = spark::GetSparkMintCoins(tx); - BOOST_CHECK(expectedCoins == info.mints); + std::vector resultedCoins; + resultedCoins.reserve(info.mints.size()); + for (auto& mint : info.mints) + resultedCoins.emplace_back(mint.first); + BOOST_CHECK(expectedCoins == resultedCoins); // spend txs.clear(); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 870d1be3ae..dfd957b0c8 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -122,7 +122,7 @@ TestingSetup::TestingSetup(const std::string& chainName, std::string suf) : Basi pwalletMain->SetBestChain(chainActive.GetLocator()); pwalletMain->zwallet = std::make_unique(pwalletMain->strWalletFile); - pwalletMain->sparkWallet = std::make_unique(pwalletMain->strWalletFile); + pwalletMain->sparkWallet = std::make_unique(pwalletMain->strWalletFile, 0); pwalletMain->zwallet->GetTracker().Init(); pwalletMain->zwallet->LoadMintPoolFromDB(); diff --git a/src/validation.cpp b/src/validation.cpp index 8ae63461f4..d91e223b95 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -3855,11 +3855,14 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, LogPrintf("HDmint: UpdateSpendStateFromBlock. [height: %d]\n", GetHeight()); pwalletMain->zwallet->GetTracker().UpdateMintStateFromBlock(blockConnecting.lelantusTxInfo->mints); } + } + + if (!GetBoolArg("-disablewallet", false) && pwalletMain->sparkWallet && blockConnecting.sparkTxInfo) { if (blockConnecting.sparkTxInfo->spentLTags.size() > 0) { LogPrintf("SparkWallet: UpdateSpendStateFromBlock. [height: %d]\n", GetHeight()); - pwalletMain->sparkWallet->UpdateSpendStateFromBlock(blockConnecting); } + pwalletMain->sparkWallet->UpdateSpendStateFromBlock(blockConnecting); if (blockConnecting.sparkTxInfo->mints.size() > 0) { LogPrintf("SparkWallet: UpdateSpendStateFromBlock. [height: %d]\n", GetHeight()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 411414f169..537734363d 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2615,7 +2615,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const { - if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + if (IsCoinBase() && !this->tx->IsSparkMint() && GetBlocksToMaturity() > 0 && IsInMainChain()) { if (fUseCache && fImmatureCreditCached) return nImmatureCreditCached; @@ -7283,9 +7283,13 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart); if (pwalletMain->IsHDSeedAvailable()) { walletInstance->zwallet = std::make_unique(pwalletMain->strWalletFile); - + int nTxHeight; + { + LOCK(cs_main); + nTxHeight = chainActive.Height(); + } // if it is first run, we need to generate the full key set for spark, if not we are loading spark wallet from db - walletInstance->sparkWallet = std::make_unique(pwalletMain->strWalletFile); + walletInstance->sparkWallet = std::make_unique(pwalletMain->strWalletFile, nTxHeight); spark::Address address = walletInstance->sparkWallet->getDefaultAddress(); unsigned char network = spark::GetNetworkType(); From 484e80bc9054862283a08c8e56ef339ce53c2d66 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Tue, 19 Nov 2024 21:09:10 +0400 Subject: [PATCH 6/8] Bring back accidentally removed line --- src/rpc/rpcevo.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 0bb1a9e441..8b86b338b1 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -898,6 +898,7 @@ UniValue protx_revoke(const JSONRPCRequest& request) // Using funds from previousely specified operator payout address CTxDestination txDest; ExtractDestination(dmn->pdmnState->scriptOperatorPayout, txDest); + FundSpecialTx(pwallet, tx, ptx, txDest); } else if (dmn->pdmnState->scriptPayout != CScript()) { // Using funds from previousely specified znode payout address CTxDestination txDest; From 2738e20bbc55ad1bac98a8a10b77dc97ca7c0ec1 Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 25 Nov 2024 09:43:55 +0400 Subject: [PATCH 7/8] Review comments applied, and more --- src/chain.h | 24 +++++++++++++++------ src/evo/deterministicmns.cpp | 4 ++-- src/libspark/coin.cpp | 4 ++++ src/libspark/keys.cpp | 2 +- src/libspark/keys.h | 2 +- src/libspark/test/mint_transaction_test.cpp | 1 + src/masternode-payments.cpp | 3 ++- src/miner.cpp | 2 +- src/rpc/mining.cpp | 10 ++++++--- src/rpc/rpcevo.cpp | 18 ++++++++++------ src/spark/sparkwallet.cpp | 8 ++++++- src/spark/sparkwallet.h | 3 ++- src/spark/state.cpp | 8 ++++--- src/spark/state.h | 2 +- src/test/spark_tests.cpp | 1 + src/wallet/test/spark_tests.cpp | 1 + 16 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/chain.h b/src/chain.h index ef5304c02e..269c7c48cb 100644 --- a/src/chain.h +++ b/src/chain.h @@ -563,12 +563,24 @@ class CDiskBlockIndex : public CBlockIndex if (nHeight >=params.nSparkCoinbase) { READWRITE(sparkMintedCoins); } else { - std::map> sparkCoins; - READWRITE(sparkCoins); - for (auto& itr : sparkCoins) { - sparkMintedCoins[itr.first].reserve(itr.second.size()); - for (auto& mint : itr.second) - sparkMintedCoins[itr.first].emplace_back(std::make_pair(mint, false)); + + if (ser_action.ForRead()) + { + std::map> sparkCoins; + READWRITE(sparkCoins); + for (auto& itr : sparkCoins) { + sparkMintedCoins[itr.first].reserve(itr.second.size()); + for (auto& mint : itr.second) + sparkMintedCoins[itr.first].emplace_back(std::make_pair(mint, false)); + } + } else { + std::map> sparkCoins; + for (auto& itr : sparkMintedCoins) { + sparkCoins[itr.first].reserve(itr.second.size()); + for (auto& mint : itr.second) + sparkCoins[itr.first].emplace_back(mint.first); + } + READWRITE(sparkCoins); } } READWRITE(sparkSetHash); diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 59b387096d..a2773a2318 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -33,7 +33,7 @@ std::string CDeterministicMNState::ToString() const } else { std::string strScriptPayout = spark::ToStringSparkAddress(scriptPayout); if (!strScriptPayout.empty()) - payoutAddress = strScriptPayout; + payoutAddress = std::move(strScriptPayout); } if (ExtractDestination(scriptOperatorPayout, dest)) { @@ -41,7 +41,7 @@ std::string CDeterministicMNState::ToString() const } else { std::string strScriptPayout = spark::ToStringSparkAddress(scriptOperatorPayout); if (!strScriptPayout.empty()) - operatorPayoutAddress = strScriptPayout; + operatorPayoutAddress = std::move(strScriptPayout); } return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, " diff --git a/src/libspark/coin.cpp b/src/libspark/coin.cpp index 8ea1a96908..4da8514e41 100644 --- a/src/libspark/coin.cpp +++ b/src/libspark/coin.cpp @@ -225,6 +225,10 @@ void Coin::setParams(const Params* params) { } bool Coin::isValidMNPayment(const spark::Address& addr, const std::vector& serialContext) const { + if (this->type != COIN_TYPE_COINBASE) { + return false; + } + Coin c(this->params, COIN_TYPE_COINBASE, k, addr, v, "BlockReward", serial_context); return this->getHash() == c.getHash(); } diff --git a/src/libspark/keys.cpp b/src/libspark/keys.cpp index a568d539be..3bae4077a6 100644 --- a/src/libspark/keys.cpp +++ b/src/libspark/keys.cpp @@ -243,7 +243,7 @@ unsigned char Address::decode(const std::string& str) { return network; } -std::vector Address::toByteVector(const unsigned char network) const { +std::vector Address::toByteVector(unsigned char network) const { std::string strAddr = encode(network); return std::vector(strAddr.begin(), strAddr.end()); } diff --git a/src/libspark/keys.h b/src/libspark/keys.h index f51213c7a0..654d3b9bc4 100644 --- a/src/libspark/keys.h +++ b/src/libspark/keys.h @@ -82,7 +82,7 @@ class Address { std::string encode(const unsigned char network) const; unsigned char decode(const std::string& str); - std::vector toByteVector(const unsigned char network) const; + std::vector toByteVector(unsigned char network) const; unsigned char fromByteVector(const std::vector& vch); private: diff --git a/src/libspark/test/mint_transaction_test.cpp b/src/libspark/test/mint_transaction_test.cpp index cbf562415f..ce9a1c16fa 100644 --- a/src/libspark/test/mint_transaction_test.cpp +++ b/src/libspark/test/mint_transaction_test.cpp @@ -40,6 +40,7 @@ BOOST_AUTO_TEST_CASE(generate_verify) output.address = Address(incoming_view_key, 12345 + j); output.v = 678 + j; output.memo = "Spam and eggs"; + output.type = 0; outputs.emplace_back(output); } diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 42a85fca9f..11b2631f83 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -141,7 +141,8 @@ std::string GetRequiredPaymentsString(int nBlockHeight, const CDeterministicMNCP std::string strScriptPayout = spark::ToStringSparkAddress(payee->pdmnState->scriptPayout); if (!strScriptPayout.empty()) strPayee = strScriptPayout; - assert(false); + else + assert(false); } else { strPayee = CBitcoinAddress(dest).ToString(); } diff --git a/src/miner.cpp b/src/miner.cpp index bc01240661..a94ef1569d 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -302,7 +302,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc try { mintedCoinData.address.fromByteVector(vch); } catch (const std::exception &) { - throw std::runtime_error(strprintf("Invalid Spark address")); + throw std::runtime_error("Invalid Spark address"); } mintedCoinData.memo = "BlockReward"; spark_outputs.push_back(mintedCoinData); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 561a8decc5..d963c64d84 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -843,9 +843,13 @@ UniValue getblocktemplate(const JSONRPCRequest& request) if (spark::IsPayToSparkAddress(txout.scriptPubKey, sparkAddr)) { strAddr = sparkAddr.encode(network); } else { - ExtractDestination(txout.scriptPubKey, address1); - CBitcoinAddress address2(address1); - strAddr = address2.ToString(); + if (ExtractDestination(txout.scriptPubKey, address1)) { + CBitcoinAddress address2(address1); + strAddr = address2.ToString(); + } else { + // Handle the error case appropriately + strAddr = "Unknown"; + } } UniValue obj(UniValue::VOBJ); diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 8b86b338b1..9f45f457c8 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -171,7 +171,7 @@ spark::Address parseSparkAddress(const std::string strAddr) { LOCK(cs_main); nTxHeight = chainActive.Height(); } - if (nTxHeight > ::Params().GetConsensus().nSparkCoinbase) + if (nTxHeight < ::Params().GetConsensus().nSparkCoinbase) throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Spark Coinbase is not active")); return sPayoutAddress; @@ -495,7 +495,7 @@ UniValue protx_register(const JSONRPCRequest& request) parseSparkAddress(request.params[paramIdx + 5].get_str()); isSparkAddress = true; } catch (const std::invalid_argument &) { - //contiune + //continue } CBitcoinAddress payoutAddress(request.params[paramIdx + 5].get_str()); if (!isSparkAddress && !payoutAddress.IsValid()) { @@ -673,14 +673,13 @@ UniValue protx_update_service(const JSONRPCRequest& request) if (request.params[4].get_str().empty()) { ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; } else { - bool isSparkAddress = false; const spark::Params* params = spark::Params::get_default(); spark::Address sPayoutAddress(params); try { parseSparkAddress(request.params[4].get_str()); isSparkAddress = true; } catch (const std::invalid_argument &) { - //contiune + //continue } CBitcoinAddress payoutAddress(request.params[4].get_str()); if (!isSparkAddress && !payoutAddress.IsValid()) { @@ -784,7 +783,7 @@ UniValue protx_update_registrar(const JSONRPCRequest& request) parseSparkAddress(request.params[4].get_str()); isSparkAddress = true; } catch (const std::invalid_argument &) { - //contiune + //continue } CBitcoinAddress payoutAddress(request.params[4].get_str()); @@ -886,7 +885,7 @@ UniValue protx_revoke(const JSONRPCRequest& request) tx.nType = TRANSACTION_PROVIDER_UPDATE_REVOKE; if (request.params.size() <= 4 && !spark::IsPayToSparkAddress(dmn->pdmnState->scriptOperatorPayout) && !spark::IsPayToSparkAddress(dmn->pdmnState->scriptPayout)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("You need to provide fee source address, payout and operator payout addresses are spark.")); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("You need to provide fee source address,in case payout and operator payout addresses are spark.")); } if (request.params.size() > 4) { @@ -963,7 +962,12 @@ static bool CheckWalletOwnsScript(CWallet* pwallet, const CScript& script) { if (!pwallet->sparkWallet) { return false; } - if (pwallet->sparkWallet->isAddressMine(std::string(script.begin() + 2, script.end() - 1))) + const spark::Params* params = spark::Params::get_default(); + spark::Address addr(params); + if (!spark::IsPayToSparkAddress(script, addr)) + return false; + + if (pwallet->sparkWallet->isAddressMine(addr)) return true; return false; #endif diff --git a/src/spark/sparkwallet.cpp b/src/spark/sparkwallet.cpp index 1ce8106888..e1508717a8 100644 --- a/src/spark/sparkwallet.cpp +++ b/src/spark/sparkwallet.cpp @@ -13,7 +13,7 @@ const uint32_t DEFAULT_SPARK_NCOUNT = 1; -CSparkWallet::CSparkWallet(const std::string& strWalletFile, const uint32_t& height) { +CSparkWallet::CSparkWallet(const std::string& strWalletFile, uint32_t height) { CWalletDB walletdb(strWalletFile); this->strWalletFile = strWalletFile; @@ -278,6 +278,12 @@ bool CSparkWallet::isAddressMine(const std::string& encodedAddr) { return false; } + + + return isAddressMine(address); +} + +bool CSparkWallet::isAddressMine(const spark::Address& address) { for (const auto& itr : addresses) { if (itr.second.get_Q1() == address.get_Q1() && itr.second.get_Q2() == address.get_Q2()) return true; diff --git a/src/spark/sparkwallet.h b/src/spark/sparkwallet.h index bb3d0b3bf8..25395a7dc2 100644 --- a/src/spark/sparkwallet.h +++ b/src/spark/sparkwallet.h @@ -22,7 +22,7 @@ const uint32_t SPARK_CHANGE_D = 0x270F; class CSparkWallet { public: - CSparkWallet(const std::string& strWalletFile, const uint32_t& height); + CSparkWallet(const std::string& strWalletFile, uint32_t height); ~CSparkWallet(); // increment diversifier and generate address for that spark::Address generateNextAddress(); @@ -44,6 +44,7 @@ class CSparkWallet { // get address for a diversifier spark::Address getAddress(const int32_t& i); bool isAddressMine(const std::string& encodedAddr); + bool isAddressMine(const spark::Address& address); bool isChangeAddress(const uint64_t& i) const; // list spark mint, mint metadata in memory and in db should be the same at this moment, so get from memory diff --git a/src/spark/state.cpp b/src/spark/state.cpp index 3b4eb3326f..e3fbf40278 100644 --- a/src/spark/state.cpp +++ b/src/spark/state.cpp @@ -87,7 +87,7 @@ bool IsPayToSparkAddress(const CScript& script) } bool IsPayToSparkAddress(const CScript& script, spark::Address& addr) -{ if (script.empty() || script[script.size()-1] != OP_SPARKMINT) +{ if (script.empty() || script.back() != OP_SPARKMINT || script.size() < 3) return false; unsigned char network = spark::GetNetworkType(); unsigned char coinNetwork; @@ -102,7 +102,7 @@ bool IsPayToSparkAddress(const CScript& script, spark::Address& addr) return network == coinNetwork; } -std::string ToStringSparkAddress(const CScript script) { +std::string ToStringSparkAddress(const CScript& script) { if (script.empty()) return ""; @@ -921,8 +921,10 @@ std::vector getSerialContext(const CTransaction &tx) { return std::vector(); int height = sparkState.GetMintedCoinHeightAndId(coins[0]).first; + if (height <= 0) + return std::vector(); // get the previous block - CBlockIndex *mintBlock = chainActive[height - 1]; + const CBlockIndex *mintBlock = chainActive[height - 1]; serialContextStream << *mintBlock->phashBlock; } else { for (auto input: tx.vin) { diff --git a/src/spark/state.h b/src/spark/state.h index 5ed35620c3..64f55a825f 100644 --- a/src/spark/state.h +++ b/src/spark/state.h @@ -43,7 +43,7 @@ bool IsSparkAllowed(int height); unsigned char GetNetworkType(); bool IsPayToSparkAddress(const CScript& script); bool IsPayToSparkAddress(const CScript& script, spark::Address& addr); -std::string ToStringSparkAddress(const CScript script); +std::string ToStringSparkAddress(const CScript& script); // Pass Scripts form mint transaction and get spark MintTransaction object diff --git a/src/test/spark_tests.cpp b/src/test/spark_tests.cpp index 85dbd1901a..3024bb49ed 100644 --- a/src/test/spark_tests.cpp +++ b/src/test/spark_tests.cpp @@ -103,6 +103,7 @@ BOOST_AUTO_TEST_CASE(parse_spark_mintscript) mintedCoin.address = address; mintedCoin.v = v; mintedCoin.memo = memo; + mintedCoin.type = 0; std::vector outputs; outputs.push_back(mintedCoin); diff --git a/src/wallet/test/spark_tests.cpp b/src/wallet/test/spark_tests.cpp index 7ed925fb2d..c8c8b3d66f 100644 --- a/src/wallet/test/spark_tests.cpp +++ b/src/wallet/test/spark_tests.cpp @@ -48,6 +48,7 @@ BOOST_AUTO_TEST_CASE(create_mint_recipient) data.address = sparkAddress; data.v = v; data.memo = "Test memo"; + data.type = 0; std::vector mintedCoins; mintedCoins.push_back(data); From 98d5fc449267c6389e00c4ebe6552a79b86425ec Mon Sep 17 00:00:00 2001 From: levonpetrosyan93 Date: Mon, 25 Nov 2024 12:29:11 +0400 Subject: [PATCH 8/8] Small cleanup --- src/miner.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/miner.cpp b/src/miner.cpp index a94ef1569d..ed9b584fda 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -315,7 +315,6 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc serialContextStream << pindexPrev->GetBlockHash(); std::vector recipients = CSparkWallet::CreateSparkMintRecipients(spark_outputs, std::vector(serialContextStream.begin(), serialContextStream.end()), true); - size_t i = 0; for (size_t i = 0; i < recipients.size(); ++i) { auto& recipient = recipients[i]; CTxOut txout(recipient.nAmount, recipient.scriptPubKey);