From 2049976a28dc7b3a8005cc42bd2be36a713a4c79 Mon Sep 17 00:00:00 2001 From: panleone Date: Fri, 7 Apr 2023 21:53:57 +0200 Subject: [PATCH] Try to stake shield notes --- src/blockassembler.cpp | 36 ++++++++-------- src/blockassembler.h | 19 +++++---- src/miner.cpp | 12 +++--- src/rpc/mining.cpp | 6 ++- src/sapling/saplingscriptpubkeyman.cpp | 5 ++- src/sapling/saplingscriptpubkeyman.h | 8 +++- src/test/test_pivx.cpp | 20 +++++---- src/wallet/test/pos_validations_tests.cpp | 35 +++++++++------- src/wallet/wallet.cpp | 51 +++++++++++++++++++---- src/wallet/wallet.h | 16 ++++--- 10 files changed, 136 insertions(+), 72 deletions(-) diff --git a/src/blockassembler.cpp b/src/blockassembler.cpp index 94f64ea1925a81..431ca67ac4dcf5 100644 --- a/src/blockassembler.cpp +++ b/src/blockassembler.cpp @@ -19,6 +19,7 @@ #include "policy/policy.h" #include "pow.h" #include "primitives/transaction.h" +#include "sapling/saplingscriptpubkeyman.h" #include "spork.h" #include "timedata.h" #include "util/system.h" @@ -66,8 +67,7 @@ static CMutableTransaction NewCoinbase(const int nHeight, const CScript* pScript return txCoinbase; } -bool SolveProofOfStake(CBlock* pblock, CBlockIndex* pindexPrev, CWallet* pwallet, - std::vector* availableCoins, bool stopPoSOnNewBlock) +bool SolveProofOfStake(CBlock* pblock, CBlockIndex* pindexPrev, CWallet* pwallet, std::vector* availableCoins, std::vector* availableShieldNotes, bool stopPoSOnNewBlock) { boost::this_thread::interruption_point(); @@ -80,12 +80,12 @@ bool SolveProofOfStake(CBlock* pblock, CBlockIndex* pindexPrev, CWallet* pwallet CMutableTransaction txCoinStake; int64_t nTxNewTime = 0; if (!pwallet->CreateCoinStake(pindexPrev, - pblock->nBits, - txCoinStake, - nTxNewTime, - availableCoins, - stopPoSOnNewBlock - )) { + pblock->nBits, + txCoinStake, + nTxNewTime, + availableCoins, + availableShieldNotes, + stopPoSOnNewBlock)) { LogPrint(BCLog::STAKING, "%s : stake not found\n", __func__); return false; } @@ -156,14 +156,15 @@ void BlockAssembler::resetBlock() } std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, - CWallet* pwallet, - bool fProofOfStake, - std::vector* availableCoins, - bool fNoMempoolTx, - bool fTestValidity, - CBlockIndex* prevBlock, - bool stopPoSOnNewBlock, - bool fIncludeQfc) + CWallet* pwallet, + bool fProofOfStake, + std::vector* availableCoins, + std::vector* availableShieldNotes, + bool fNoMempoolTx, + bool fTestValidity, + CBlockIndex* prevBlock, + bool stopPoSOnNewBlock, + bool fIncludeQfc) { resetBlock(); @@ -187,8 +188,7 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc } // Depending on the tip height, try to find a coinstake who solves the block or create a coinbase tx. - if (!(fProofOfStake ? SolveProofOfStake(pblock, pindexPrev, pwallet, availableCoins, stopPoSOnNewBlock) - : CreateCoinbaseTx(pblock, scriptPubKeyIn, pindexPrev))) { + if (!(fProofOfStake ? SolveProofOfStake(pblock, pindexPrev, pwallet, availableCoins, availableShieldNotes, stopPoSOnNewBlock) : CreateCoinbaseTx(pblock, scriptPubKeyIn, pindexPrev))) { return nullptr; } diff --git a/src/blockassembler.h b/src/blockassembler.h index 0db38ebd3f68b1..ac9c83b9ba6762 100644 --- a/src/blockassembler.h +++ b/src/blockassembler.h @@ -8,6 +8,8 @@ #define PIVX_BLOCKASSEMBLER_H #include "primitives/block.h" +#include "sapling/saplingscriptpubkeyman.h" +#include "stakeinput.h" #include "txmempool.h" #include @@ -163,14 +165,15 @@ class BlockAssembler BlockAssembler(const CChainParams& chainparams, const bool defaultPrintPriority); /** Construct a new block template with coinbase to scriptPubKeyIn */ std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn, - CWallet* pwallet = nullptr, - bool fProofOfStake = false, - std::vector* availableCoins = nullptr, - bool fNoMempoolTx = false, - bool fTestValidity = true, - CBlockIndex* prevBlock = nullptr, - bool stopPoSOnNewBlock = true, - bool fIncludeQfc = true); + CWallet* pwallet = nullptr, + bool fProofOfStake = false, + std::vector* availableCoins = nullptr, + std::vector* availableShieldNotes = nullptr, + bool fNoMempoolTx = false, + bool fTestValidity = true, + CBlockIndex* prevBlock = nullptr, + bool stopPoSOnNewBlock = true, + bool fIncludeQfc = true); private: // utility functions diff --git a/src/miner.cpp b/src/miner.cpp index ce42f821a67215..2767dfa999d473 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -96,7 +96,7 @@ bool ProcessBlockFound(const std::shared_ptr& pblock, CWallet& wal bool fGenerateBitcoins = false; bool fStakeableCoins = false; -void CheckForCoins(CWallet* pwallet, std::vector* availableCoins) +void CheckForCoins(CWallet* pwallet, std::vector* availableCoins, std::vector* availableShieldNotes) { if (!pwallet || !pwallet->pStakerStatus) return; @@ -108,6 +108,7 @@ void CheckForCoins(CWallet* pwallet, std::vector* availableCoi return; } fStakeableCoins = pwallet->StakeableCoins(availableCoins); + pwallet->StakeableShieldNotes(availableShieldNotes); } void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) @@ -123,6 +124,7 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) // Available UTXO set std::vector availableCoins; + std::vector availableShieldNotes; unsigned int nExtraNonce = 0; while (fGenerateBitcoins || fProofOfStake) { @@ -139,13 +141,13 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) } // update fStakeableCoins - CheckForCoins(pwallet, &availableCoins); + CheckForCoins(pwallet, &availableCoins, &availableShieldNotes); while ((g_connman && g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 && Params().MiningRequiresPeers()) || pwallet->IsLocked() || !fStakeableCoins || masternodeSync.NotCompleted()) { MilliSleep(5000); // Do another check here to ensure fStakeableCoins is updated - if (!fStakeableCoins) CheckForCoins(pwallet, &availableCoins); + if (!fStakeableCoins) CheckForCoins(pwallet, &availableCoins, &availableShieldNotes); } //search our map of hashed blocks, see if bestblock has been hashed yet @@ -168,8 +170,8 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake) unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated(); std::unique_ptr pblocktemplate((fProofOfStake ? - BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), pwallet, true, &availableCoins) : - CreateNewBlockWithKey(pReservekey, pwallet))); + BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), pwallet, true, &availableCoins, &availableShieldNotes) : + CreateNewBlockWithKey(pReservekey, pwallet))); if (!pblocktemplate) continue; std::shared_ptr pblock = std::make_shared(pblocktemplate->block); diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 4ce1dadd78227a..ebaf9672bb591d 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -42,13 +42,15 @@ UniValue generateBlocks(const Consensus::Params& consensus, // Get available coins std::vector availableCoins; + std::vector availableShieldNotes; + pwallet->StakeableShieldNotes(&availableShieldNotes); if (fPoS && !pwallet->StakeableCoins(&availableCoins)) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "No available coins to stake"); } std::unique_ptr pblocktemplate(fPoS ? - BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), pwallet, true, &availableCoins) : - CreateNewBlockWithScript(*coinbaseScript, pwallet)); + BlockAssembler(Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(CScript(), pwallet, true, &availableCoins, &availableShieldNotes) : + CreateNewBlockWithScript(*coinbaseScript, pwallet)); if (!pblocktemplate.get()) break; std::shared_ptr pblock = std::make_shared(pblocktemplate->block); diff --git a/src/sapling/saplingscriptpubkeyman.cpp b/src/sapling/saplingscriptpubkeyman.cpp index 36f3243612518f..61921665dce1b3 100644 --- a/src/sapling/saplingscriptpubkeyman.cpp +++ b/src/sapling/saplingscriptpubkeyman.cpp @@ -541,7 +541,7 @@ void SaplingScriptPubKeyMan::GetFilteredNotes( } } } -void SaplingScriptPubKeyMan::GetStakableNotes(std::vector& shieldInputs, int minDepth) +void SaplingScriptPubKeyMan::GetStakeableNotes(std::vector* shieldInputs, int minDepth) { LOCK(wallet->cs_wallet); for (auto& p : wallet->mapWallet) { @@ -580,7 +580,8 @@ void SaplingScriptPubKeyMan::GetStakableNotes(std::vector& shieldI if (nd.nullifier && IsSaplingSpent(*nd.nullifier)) { continue; } - shieldInputs.emplace_back(CShieldStake(note.value(), *nd.nullifier)); + SaplingNoteEntry noteEntry = SaplingNoteEntry(op, pa, note, notePt.memo(), depth); + shieldInputs->emplace_back(CStakeableShieldNote(noteEntry, *nd.nullifier)); } } } diff --git a/src/sapling/saplingscriptpubkeyman.h b/src/sapling/saplingscriptpubkeyman.h index 830826da33d95f..0c9c71407f3a74 100644 --- a/src/sapling/saplingscriptpubkeyman.h +++ b/src/sapling/saplingscriptpubkeyman.h @@ -37,6 +37,12 @@ struct SaplingNoteEntry int confirmations; }; +struct CStakeableShieldNote { + explicit CStakeableShieldNote(const SaplingNoteEntry& _note, const uint256 _nullifier) : note(_note), nullifier(_nullifier) {} + SaplingNoteEntry note; + uint256 nullifier; +}; + class SaplingNoteData { public: @@ -295,7 +301,7 @@ class SaplingScriptPubKeyMan { bool ignoreLocked=true) const; /* Return a list of notes that are stakable */ - void GetStakableNotes(std::vector& shieldInputs, int minDepth); + void GetStakeableNotes(std::vector* shieldInputs, int minDepth); /* Return list of available notes grouped by sapling address. */ std::map> ListNotes() const; diff --git a/src/test/test_pivx.cpp b/src/test/test_pivx.cpp index 336a4773953be3..c57644076b8c7d 100644 --- a/src/test/test_pivx.cpp +++ b/src/test/test_pivx.cpp @@ -201,15 +201,17 @@ CBlock TestChainSetup::CreateBlock(const std::vector& txns, CBlockIndex* customPrevBlock) { std::unique_ptr pblocktemplate = BlockAssembler( - Params(), DEFAULT_PRINTPRIORITY).CreateNewBlock(scriptPubKey, - nullptr, // wallet - false, // fProofOfStake - nullptr, // availableCoins - fNoMempoolTx, - fTestBlockValidity, - customPrevBlock, - true, - fIncludeQfc); + Params(), DEFAULT_PRINTPRIORITY) + .CreateNewBlock(scriptPubKey, + nullptr, // wallet + false, // fProofOfStake + nullptr, // availableCoins + nullptr, // availableShieldCoins + fNoMempoolTx, + fTestBlockValidity, + customPrevBlock, + true, + fIncludeQfc); std::shared_ptr pblock = std::make_shared(pblocktemplate->block); // Add passed-in txns: diff --git a/src/wallet/test/pos_validations_tests.cpp b/src/wallet/test/pos_validations_tests.cpp index e07d89d1c58829..a588306e5face6 100644 --- a/src/wallet/test/pos_validations_tests.cpp +++ b/src/wallet/test/pos_validations_tests.cpp @@ -44,13 +44,17 @@ BOOST_FIXTURE_TEST_CASE(coinstake_tests, TestPoSChainSetup) // Let's create the block std::vector availableCoins; + std::vector availableShieldNotes; BOOST_CHECK(pwalletMain->StakeableCoins(&availableCoins)); + pwalletMain->StakeableShieldNotes(&availableShieldNotes); std::unique_ptr pblocktemplate = BlockAssembler( - Params(), false).CreateNewBlock(CScript(), - pwalletMain.get(), - true, - &availableCoins, - true); + Params(), false) + .CreateNewBlock(CScript(), + pwalletMain.get(), + true, + &availableCoins, + &availableShieldNotes, + true); std::shared_ptr pblock = std::make_shared(pblocktemplate->block); BOOST_CHECK(pblock->IsProofOfStake()); @@ -143,8 +147,9 @@ std::shared_ptr CreateBlockInternal(CWallet* pwalletMain, const std::vec std::initializer_list> forkchain = {}) { std::vector availableCoins; + std::vector availableShieldNotes; BOOST_CHECK(pwalletMain->StakeableCoins(&availableCoins)); - + pwalletMain->StakeableShieldNotes(&availableShieldNotes); // Remove any utxo which is not deeper than 120 blocks (for the same reasoning // used when selecting tx inputs in CreateAndCommitTx) // Also, as the wallet is not prepared to follow several chains at the same time, @@ -160,14 +165,16 @@ std::shared_ptr CreateBlockInternal(CWallet* pwalletMain, const std::vec } std::unique_ptr pblocktemplate = BlockAssembler( - Params(), false).CreateNewBlock(CScript(), - pwalletMain, - true, - &availableCoins, - true, - false, - customPrevBlock, - false); + Params(), false) + .CreateNewBlock(CScript(), + pwalletMain, + true, + &availableCoins, + &availableShieldNotes, + true, + false, + customPrevBlock, + false); BOOST_ASSERT(pblocktemplate); auto pblock = std::make_shared(pblocktemplate->block); if (!txns.empty()) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c13f8ba19f426a..71cf9390d754c8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2695,6 +2695,12 @@ bool CWallet::StakeableCoins(std::vector* pCoins) return (pCoins && !pCoins->empty()); } +void CWallet::StakeableShieldNotes(std::vector* shieldCoins) +{ + shieldCoins->clear(); + m_sspk_man->GetStakeableNotes(shieldCoins, Params().GetConsensus().nStakeMinDepth); +} + bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const { setCoinsRet.clear(); @@ -3309,12 +3315,13 @@ bool CWallet::CreateCoinstakeOuts(const CPivStake& stakeInput, std::vector* availableCoins, - bool stopOnNewBlock) const + const CBlockIndex* pindexPrev, + unsigned int nBits, + CMutableTransaction& txNew, + int64_t& nTxNewTime, + std::vector* availableCoins, + std::vector* availableShieldNotes, + bool stopOnNewBlock) const { // shuffle coins if (availableCoins && Params().IsRegTestNet()) { @@ -3336,6 +3343,7 @@ bool CWallet::CreateCoinStake( CScript scriptPubKeyKernel; bool fKernelFound = false; int nAttempts = 0; + int nShieldAttempts = 0; for (auto it = availableCoins->begin(); it != availableCoins->end();) { COutPoint outPoint = COutPoint(it->tx->GetHash(), it->i); CPivStake stakeInput(it->tx->tx->vout[it->i], @@ -3409,9 +3417,36 @@ bool CWallet::CreateCoinStake( if (nBytes >= DEFAULT_BLOCK_MAX_SIZE / 5) return error("%s : exceeded coinstake size limit", __func__); - break; + LogPrint(BCLog::STAKING, "%s: shield attempted staking %d times\n", __func__, nShieldAttempts); + return fKernelFound; + } + LogPrint(BCLog::STAKING, "%s: transparent attempted staking %d times\n", __func__, nAttempts); + // Resetting tx values + txNew.vin.clear(); + txNew.vout.clear(); + for (auto it = availableShieldNotes->begin(); it != availableShieldNotes->end();) { + CShieldStake noteStake = CShieldStake(it->note.note.value(), it->nullifier); + // New block came in, move on + if (stopOnNewBlock && GetLastBlockHeightLockWallet() != pindexPrev->nHeight) return false; + + // Make sure the wallet is unlocked and shutdown hasn't been requested + if (IsLocked() || ShutdownRequested()) return false; + + // TODO: check that the note IS NOT spent + nShieldAttempts++; + nCredit = 0; + bool fKernelFound = false; + fKernelFound = Stake(pindexPrev, ¬eStake, nBits, nTxNewTime); + // update staker status (time, attempts) + pStakerStatus->SetLastTime(nTxNewTime); + pStakerStatus->SetLastTries(nAttempts + nShieldAttempts); + + if (!fKernelFound) { + it++; + continue; + } } - LogPrint(BCLog::STAKING, "%s: attempted staking %d times\n", __func__, nAttempts); + LogPrint(BCLog::STAKING, "%s: shield attempted staking %d times\n", __func__, nShieldAttempts); return fKernelFound; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index c2f7bfb3c2ffa7..0c8bcfd5f404b1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -99,6 +99,7 @@ class ScriptPubKeyMan; class SaplingScriptPubKeyMan; class SaplingNoteData; struct SaplingNoteEntry; +struct CStakeableShieldNote; /** (client) version numbers for particular wallet features */ enum WalletFeature { @@ -835,6 +836,10 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector vCoins, std::set >& setCoinsRet, CAmount& nValueRet) const; //! >> Available coins (staking) bool StakeableCoins(std::vector* pCoins = nullptr); + + //! >> Available coins (shield staking) + void StakeableShieldNotes(std::vector* shieldCoins); + //! >> Available coins (P2CS) void GetAvailableP2CSCoins(std::vector& vCoins) const; @@ -1100,11 +1105,12 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool CreateCoinstakeOuts(const CPivStake& stakeInput, std::vector& vout, CAmount nTotal) const; bool CreateCoinStake(const CBlockIndex* pindexPrev, - unsigned int nBits, - CMutableTransaction& txNew, - int64_t& nTxNewTime, - std::vector* availableCoins, - bool stopOnNewBlock = true) const; + unsigned int nBits, + CMutableTransaction& txNew, + int64_t& nTxNewTime, + std::vector* availableCoins, + std::vector* availableShieldNotes, + bool stopOnNewBlock = true) const; bool SignCoinStake(CMutableTransaction& txNew) const; void AutoCombineDust(CConnman* connman);