diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h index ddeb931112..98cd949449 100644 --- a/src/wallet/transaction.h +++ b/src/wallet/transaction.h @@ -342,6 +342,7 @@ class CWalletTx const Txid& GetHash() const LIFETIMEBOUND { return tx->GetHash(); } const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return tx->GetWitnessHash(); } bool IsCoinBase() const { return tx->IsCoinBase(); } + bool IsCoinStake() const { return tx->IsCoinStake(); } private: // Disable copying of CWalletTx objects to prevent bugs where instances get diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3ac09430d8..eeb8becd0a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -776,6 +776,23 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid, Walle SyncMetaData(range); } +void CWallet::RemoveFromSpends(const COutPoint& outpoint, const uint256& wtxid) +{ + std::pair range; + range = mapTxSpends.equal_range(outpoint); + TxSpends::iterator it = range.first; + for(; it != range.second; ++ it) + { + if(it->second == wtxid) + { + mapTxSpends.erase(it); + break; + } + } + range = mapTxSpends.equal_range(outpoint); + if(range.first != range.second) + SyncMetaData(range); +} void CWallet::AddToSpends(const CWalletTx& wtx, WalletBatch* batch) { @@ -786,6 +803,15 @@ void CWallet::AddToSpends(const CWalletTx& wtx, WalletBatch* batch) AddToSpends(txin.prevout, wtx.GetHash(), batch); } +void CWallet::RemoveFromSpends(const CWalletTx& wtx) +{ + if (wtx.IsCoinBase()) // Coinbases don't spend anything! + return; + + for(const CTxIn& txin : wtx.tx->vin) + RemoveFromSpends(txin.prevout, wtx.GetHash()); +} + bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) @@ -1306,7 +1332,9 @@ bool CWallet::AbandonTransaction(const uint256& hashTx) auto try_updating_state = [](CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { // If the orig tx was not in block/mempool, none of its spends can be. - assert(!wtx.isConfirmed()); + if (!wtx.IsCoinStake()) { + assert(!wtx.isConfirmed()); + } assert(!wtx.InMempool()); // If already conflicted or abandoned, no need to set abandoned if (!wtx.isConflicted() && !wtx.isAbandoned()) { @@ -3356,6 +3384,22 @@ bool CWallet::BackupWallet(const std::string& strDest) const return GetDatabase().Backup(strDest); } +bool CWallet::LoadToken(const CTokenInfo &token) +{ + uint256 hash = token.GetHash(); + mapToken[hash] = token; + + return true; +} + +bool CWallet::LoadTokenTx(const CTokenTx &tokenTx) +{ + uint256 hash = tokenTx.GetHash(); + mapTokenTx[hash] = tokenTx; + + return true; +} + CKeyPool::CKeyPool() { nTime = GetTime(); @@ -3389,15 +3433,16 @@ int CWallet::GetTxBlocksToMaturity(const CWalletTx& wtx) const { AssertLockHeld(cs_wallet); - if (!wtx.IsCoinBase()) { + if (!(wtx.IsCoinBase() || wtx.IsCoinStake())) { return 0; } int chain_depth = GetTxDepthInMainChain(wtx); - assert(chain_depth >= 0); // coinbase tx should not be conflicted - return std::max(0, (COINBASE_MATURITY+1) - chain_depth); + int nHeight = GetLastBlockHeight() + 1; + int coinbaseMaturity = Params().GetConsensus().CoinbaseMaturity(nHeight); + return std::max(0, (coinbaseMaturity+1) - chain_depth); } -bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const +bool CWallet::IsTxImmature(const CWalletTx& wtx) const { AssertLockHeld(cs_wallet); @@ -3405,6 +3450,16 @@ bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const return GetTxBlocksToMaturity(wtx) > 0; } +bool CWallet::IsTxImmatureCoinBase(const CWalletTx& wtx) const +{ + return wtx.IsCoinBase() && IsTxImmature(wtx); +} + +bool CWallet::IsTxImmatureCoinStake(const CWalletTx& wtx) const +{ + return wtx.IsCoinStake() && IsTxImmature(wtx); +} + bool CWallet::IsCrypted() const { return HasEncryptionKeys(); @@ -4457,6 +4512,26 @@ util::Result MigrateLegacyToDescriptor(const std::string& walle return res; } +uint256 CTokenInfo::GetHash() const +{ + return (HashWriter{} << SER_INFO_GETHASH(*this)).GetHash(); +} + +uint256 CTokenTx::GetHash() const +{ + return (HashWriter{} << SER_INFO_GETHASH(*this)).GetHash(); +} + +uint256 CDelegationInfo::GetHash() const +{ + return (HashWriter{} << SER_INFO_GETHASH(*this)).GetHash(); +} + +uint256 CSuperStakerInfo::GetHash() const +{ + return (HashWriter{} << SER_INFO_GETHASH(*this)).GetHash(); +} + void CWallet::CacheNewScriptPubKeys(const std::set& spks, ScriptPubKeyMan* spkm) { for (const auto& script : spks) { @@ -4469,4 +4544,39 @@ void CWallet::TopUpCallback(const std::set& spks, ScriptPubKeyMan* spkm // Update scriptPubKey cache CacheNewScriptPubKeys(spks, spkm); } + +bool CWallet::LoadContractData(const std::string &address, const std::string &key, const std::string &value) +{ + bool ret = true; + if(key == "name") + { + mapContractBook[address].name = value; + } + else if(key == "abi") + { + mapContractBook[address].abi = value; + } + else + { + ret = false; + } + return ret; +} + +bool CWallet::LoadDelegation(const CDelegationInfo &delegation) +{ + uint256 hash = delegation.GetHash(); + mapDelegation[hash] = delegation; + + return true; +} + +bool CWallet::LoadSuperStaker(const CSuperStakerInfo &superStaker) +{ + uint256 hash = superStaker.GetHash(); + mapSuperStaker[hash] = superStaker; + + return true; +} + } // namespace wallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index d009661880..1d16351010 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -50,6 +50,7 @@ #include #include +#include class CKey; class CKeyID; @@ -314,6 +315,13 @@ struct CRecipient bool fSubtractFeeFromAmount; }; +struct CScriptCache{ + bool contract = false; + bool keyIdOk = false; + uint160 keyId; + bool solvable = false; +}; + class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime /** * A CWallet maintains a set of transactions and balances, and provides the ability to create new transactions. @@ -356,7 +364,9 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati typedef std::unordered_multimap TxSpends; TxSpends mapTxSpends GUARDED_BY(cs_wallet); void AddToSpends(const COutPoint& outpoint, const uint256& wtxid, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void RemoveFromSpends(const COutPoint& outpoint, const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void AddToSpends(const CWalletTx& wtx, WalletBatch* batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void RemoveFromSpends(const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Add a transaction to the wallet, or update it. confirm.block_* should @@ -461,6 +471,7 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * This lock protects all the fields added by CWallet. */ mutable RecursiveMutex cs_wallet; + mutable RecursiveMutex cs_worker; WalletDatabase& GetDatabase() const override { @@ -515,6 +526,22 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * but also shouldn't try to use it again. */ std::set setLockedCoins GUARDED_BY(cs_wallet); + std::map mapContractBook; + + std::map mapToken; + + std::map mapTokenTx; + + std::map mapDelegation; + + std::map mapSuperStaker; + + bool fUpdatedSuperStaker = false; + + std::map mapAddressUnspentCache; + + bool fUpdateAddressUnspentCache = false; + /** Registered interfaces::Chain::Notifications handler. */ std::unique_ptr m_chain_notifications_handler; @@ -551,7 +578,9 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * >0 : is a coinbase transaction which matures in this many blocks */ int GetTxBlocksToMaturity(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool IsTxImmature(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsTxImmatureCoinBase(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool IsTxImmatureCoinStake(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! check whether we support the named feature bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return IsFeatureSupported(nWalletVersion, wf); } @@ -609,6 +638,13 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati void GetKeyBirthTimes(std::map &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); unsigned int ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const; + bool LoadToken(const CTokenInfo &token); + + bool LoadTokenTx(const CTokenTx &tokenTx); + + //! Adds a contract data tuple to the store, without saving it to disk + bool LoadContractData(const std::string &address, const std::string &key, const std::string &value); + /** * Increment the next transaction order id * @return next transaction order id @@ -759,6 +795,21 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati /** Notify external script when a wallet transaction comes in or is updated (handled by -walletnotify) */ std::string m_notify_tx_changed_script; + // optional setting to unlock wallet for staking only + // serves to disable the trivial sendmoney when OS account compromised + // provides no real security + std::atomic m_wallet_unlock_staking_only{false}; + bool m_use_change_address{DEFAULT_USE_CHANGE_ADDRESS}; + CAmount m_reserve_balance{DEFAULT_RESERVE_BALANCE}; + int64_t m_last_coin_stake_search_time{0}; + int64_t m_last_coin_stake_search_interval{0}; + std::atomic m_enabled_staking{false}; + CAmount m_staker_min_utxo_size{DEFAULT_STAKER_MIN_UTXO_SIZE}; + int32_t m_staker_max_utxo_script_cache{DEFAULT_STAKER_MAX_UTXO_SCRIPT_CACHE}; + uint8_t m_staking_min_fee{DEFAULT_STAKING_MIN_FEE}; + std::atomic m_stop_staking_thread{false}; + std::atomic m_is_staking_thread_stopped{false}; + size_t KeypoolCountExternalKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool TopUpKeyPool(unsigned int kpSize = 0); @@ -868,6 +919,13 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati */ boost::signals2::signal NotifyTransactionChanged; + /** + * Wallet token transaction added, removed or updated. + * @note called with lock cs_wallet held. + */ + boost::signals2::signal NotifyTokenTransactionChanged; + /** Show progress e.g. for rescan */ boost::signals2::signal ShowProgress; @@ -883,6 +941,27 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati */ boost::signals2::signal NotifyStatusChanged; + /** Wallet transaction added, removed or updated. */ + boost::signals2::signal NotifyTokenChanged; + + /** Contract book entry changed. */ + boost::signals2::signal NotifyContractBookChanged; + + /** Wallet delegation added, removed or updated. */ + boost::signals2::signal NotifyDelegationChanged; + + /** Wallet super staker added, removed or updated. */ + boost::signals2::signal NotifySuperStakerChanged; + + /** Wallet delegations staker added, removed or updated. */ + boost::signals2::signal NotifyDelegationsStakerChanged; + /** Inquire whether this wallet broadcasts transactions. */ bool GetBroadcastTransactions() const { return fBroadcastTransactions; } /** Set whether this wallet broadcasts transactions. */ @@ -1074,6 +1153,23 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati void CacheNewScriptPubKeys(const std::set& spks, ScriptPubKeyMan* spkm); void TopUpCallback(const std::set& spks, ScriptPubKeyMan* spkm) override; + + /* Load delegation entry into the wallet */ + bool LoadDelegation(const CDelegationInfo &delegation); + + /* Load super staker entry into the wallet */ + bool LoadSuperStaker(const CSuperStakerInfo &superStaker); + + std::map m_delegations_weight; + std::map m_have_coin_superstaker; + int m_num_threads = 1; + mutable boost::thread_group threads; + std::string m_ledger_id; + boost::thread_group* stakeThread = nullptr; + bool fHasMinerStakeCache = false; + mutable std::map prevoutScriptCache; + mutable std::map addressStakeCache; + std::atomic fCleanCoinStake = true; }; /** @@ -1141,6 +1237,202 @@ struct MigrationResult { //! Do all steps to migrate a legacy wallet to a descriptor wallet [[nodiscard]] util::Result MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context); + +struct InfoSerParams { + const bool get_hash; + SER_PARAMS_OPFUNC +}; +static constexpr InfoSerParams SER_INFO{.get_hash = false}; +static constexpr InfoSerParams SER_INFO_GETHASH{.get_hash = true}; + +class CTokenInfo +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + std::string strContractAddress; + std::string strTokenName; + std::string strTokenSymbol; + uint8_t nDecimals; + std::string strSenderAddress; + + // Wallet data for token transaction + int64_t nCreateTime; + uint256 blockHash; + int64_t blockNumber; + + CTokenInfo() + { + SetNull(); + } + + SERIALIZE_METHODS(CTokenInfo, obj) { + if (!s.GetParams().get_hash) + { + READWRITE(obj.nVersion, obj.nCreateTime, obj.strTokenName, obj.strTokenSymbol, obj.blockHash, obj.blockNumber); + } + READWRITE(obj.nDecimals, obj.strContractAddress, obj.strSenderAddress); + } + + void SetNull() + { + nVersion = CTokenInfo::CURRENT_VERSION; + nCreateTime = 0; + strContractAddress = ""; + strTokenName = ""; + strTokenSymbol = ""; + nDecimals = 0; + strSenderAddress = ""; + blockHash.SetNull(); + blockNumber = -1; + } + + uint256 GetHash() const; +}; + +class CTokenTx +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + std::string strContractAddress; + std::string strSenderAddress; + std::string strReceiverAddress; + uint256 nValue; + uint256 transactionHash; + + // Wallet data for token transaction + int64_t nCreateTime; + uint256 blockHash; + int64_t blockNumber; + std::string strLabel; + + CTokenTx() + { + SetNull(); + } + + SERIALIZE_METHODS(CTokenTx, obj) { + if (!s.GetParams().get_hash) + { + READWRITE(obj.nVersion, obj.nCreateTime, obj.blockHash, obj.blockNumber, LIMITED_STRING(obj.strLabel, 65536)); + } + READWRITE(obj.strContractAddress, obj.strSenderAddress, obj.strReceiverAddress, obj.nValue, obj.transactionHash); + } + + void SetNull() + { + nVersion = CTokenTx::CURRENT_VERSION; + nCreateTime = 0; + strContractAddress = ""; + strSenderAddress = ""; + strReceiverAddress = ""; + nValue.SetNull(); + transactionHash.SetNull(); + blockHash.SetNull(); + blockNumber = -1; + strLabel = ""; + } + + uint256 GetHash() const; +}; + +/** Contract book data */ +class CContractBookData +{ +public: + std::string name; + std::string abi; + + CContractBookData() + {} +}; + +class CDelegationInfo +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + int64_t nCreateTime; + uint8_t nFee; + uint160 delegateAddress; + uint160 stakerAddress; + std::string strStakerName; + int64_t blockNumber; + uint256 createTxHash; + uint256 removeTxHash; + + CDelegationInfo() + { + SetNull(); + } + + SERIALIZE_METHODS(CDelegationInfo, obj) { + if (!s.GetParams().get_hash) + { + READWRITE(obj.nVersion, obj.nCreateTime, obj.nFee, obj.blockNumber, obj.createTxHash, obj.removeTxHash); + } + READWRITE(obj.delegateAddress, obj.stakerAddress, obj.strStakerName); + } + + void SetNull() + { + nVersion = CDelegationInfo::CURRENT_VERSION; + nCreateTime = 0; + nFee = 0; + delegateAddress.SetNull(); + stakerAddress.SetNull(); + strStakerName = ""; + blockNumber = -1; + createTxHash.SetNull(); + removeTxHash.SetNull(); + } + + uint256 GetHash() const; +}; + +class CSuperStakerInfo +{ +public: + static const int CURRENT_VERSION=1; + int nVersion; + int64_t nCreateTime; + uint160 stakerAddress; + std::string strStakerName; + bool fCustomConfig; + uint8_t nMinFee; + CAmount nMinDelegateUtxo; + std::vector delegateAddressList; + int nDelegateAddressType; + + CSuperStakerInfo() + { + SetNull(); + } + + SERIALIZE_METHODS(CSuperStakerInfo, obj) { + if (!s.GetParams().get_hash) + { + READWRITE(obj.nVersion, obj.nCreateTime, obj.nMinFee, obj.fCustomConfig, obj.nMinDelegateUtxo, obj.delegateAddressList, obj.nDelegateAddressType); + } + READWRITE(obj.stakerAddress, obj.strStakerName); + } + + void SetNull() + { + nVersion = CSuperStakerInfo::CURRENT_VERSION; + nCreateTime = 0; + nMinFee = 0; + stakerAddress.SetNull(); + strStakerName = ""; + fCustomConfig = 0; + nMinDelegateUtxo = 0; + delegateAddressList.clear(); + nDelegateAddressType = 0; + } + + uint256 GetHash() const; +}; } // namespace wallet #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index b1ce7ee4e7..083820c43d 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -65,6 +65,11 @@ const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"}; const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"}; const std::string WATCHMETA{"watchmeta"}; const std::string WATCHS{"watchs"}; +const std::string TOKEN{"token"}; +const std::string TOKENTX{"tokentx"}; +const std::string CONTRACTDATA{"contractdata"}; +const std::string DELEGATION{"delegation"}; +const std::string SUPERSTAKER{"superstaker"}; const std::unordered_set LEGACY_TYPES{CRYPTED_KEY, CSCRIPT, DEFAULTKEY, HDCHAIN, KEYMETA, KEY, OLD_KEY, POOL, WATCHMETA, WATCHS}; } // namespace DBKeys @@ -451,6 +456,120 @@ bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr) return true; } +bool LoadToken(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr) +{ + LOCK(pwallet->cs_wallet); + try { + uint256 hash; + ssKey >> hash; + CTokenInfo wtoken; + ssValue >> SER_INFO(wtoken); + if (wtoken.GetHash() != hash) + { + strErr = "Error reading wallet database: CTokenInfo corrupt"; + return false; + } + pwallet->LoadToken(wtoken); + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} + +bool LoadTokenTx(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr) +{ + LOCK(pwallet->cs_wallet); + try { + uint256 hash; + ssKey >> hash; + CTokenTx wTokenTx; + ssValue >> SER_INFO(wTokenTx); + if (wTokenTx.GetHash() != hash) + { + strErr = "Error reading wallet database: CTokenTx corrupt"; + return false; + } + pwallet->LoadTokenTx(wTokenTx); + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} + +bool LoadDelegation(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr) +{ + LOCK(pwallet->cs_wallet); + try { + uint256 hash; + ssKey >> hash; + CDelegationInfo wdelegation; + ssValue >> SER_INFO(wdelegation); + if (wdelegation.GetHash() != hash) + { + strErr = "Error reading wallet database: CDelegationInfo corrupt"; + return false; + } + pwallet->LoadDelegation(wdelegation); + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} + +bool LoadSuperStaker(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr) +{ + LOCK(pwallet->cs_wallet); + try { + uint256 hash; + ssKey >> hash; + CSuperStakerInfo wsuperStaker; + ssValue >> SER_INFO(wsuperStaker); + if (wsuperStaker.GetHash() != hash) + { + strErr = "Error reading wallet database: CSuperStakerInfo corrupt"; + return false; + } + pwallet->LoadSuperStaker(wsuperStaker); + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} + +bool LoadContractData(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr) +{ + LOCK(pwallet->cs_wallet); + try { + std::string strAddress, strKey, strValue; + ssKey >> strAddress; + ssKey >> strKey; + ssValue >> strValue; + if (!pwallet->LoadContractData(strAddress, strKey, strValue)) + { + strErr = "Error reading wallet database: LoadContractData failed"; + return false; + } + } catch (const std::exception& e) { + if (strErr.empty()) { + strErr = e.what(); + } + return false; + } + return true; +} + static DBErrors LoadMinVersion(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { AssertLockHeld(pwallet->cs_wallet); @@ -631,15 +750,15 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, } // Extract the index and internal from the path - // Path string is m/0'/k'/i' - // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit + // Path string is m/88'/k'/i' + // Path vector is [88', k', i'] (but as ints OR'd with the hardened bit // k == 0 for external, 1 for internal. i is the index if (path.size() != 3) { strErr = "Error reading wallet database: keymeta found with unexpected path"; return DBErrors::NONCRITICAL_ERROR; } - if (path[0] != 0x80000000) { - strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]); + if (path[0] != 0x80000058) { + strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000058) for the element at index 0", path[0]); return DBErrors::NONCRITICAL_ERROR; } if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) { @@ -1147,6 +1266,49 @@ static DBErrors LoadDecryptionKeys(CWallet* pwallet, DatabaseBatch& batch) EXCLU return mkey_res.m_result; } +static DBErrors LoadSpecificRecords(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) +{ + AssertLockHeld(pwallet->cs_wallet); + DBErrors result = DBErrors::LOAD_OK; + + // Load token + LoadResult token_res = LoadRecords(pwallet, batch, DBKeys::TOKEN, + [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) { + return LoadToken(pwallet, key, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, token_res.m_result); + + // Load token transaction + LoadResult token_tx_res = LoadRecords(pwallet, batch, DBKeys::TOKENTX, + [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) { + return LoadTokenTx(pwallet, key, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, token_tx_res.m_result); + + // Load delegation + LoadResult delegation_res = LoadRecords(pwallet, batch, DBKeys::DELEGATION, + [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) { + return LoadDelegation(pwallet, key, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, delegation_res.m_result); + + // Load super staker + LoadResult super_staker_res = LoadRecords(pwallet, batch, DBKeys::SUPERSTAKER, + [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) { + return LoadSuperStaker(pwallet, key, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, super_staker_res.m_result); + + // Load contract data + LoadResult contract_data_res = LoadRecords(pwallet, batch, DBKeys::CONTRACTDATA, + [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) { + return LoadContractData(pwallet, key, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT; + }); + result = std::max(result, contract_data_res.m_result); + + return result; +} + DBErrors WalletBatch::LoadWallet(CWallet* pwallet) { DBErrors result = DBErrors::LOAD_OK; @@ -1195,6 +1357,9 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) // Load decryption keys result = std::max(LoadDecryptionKeys(pwallet, *m_batch), result); + + // Load qtum specific records + result = std::max(LoadSpecificRecords(pwallet, *m_batch), result); } catch (...) { // Exceptions that can be ignored or treated as non-critical are handled by the individual loading functions. // Any uncaught exceptions will be caught here and treated as critical. @@ -1347,6 +1512,56 @@ bool WalletBatch::TxnAbort() return m_batch->TxnAbort(); } +bool WalletBatch::WriteToken(const CTokenInfo &wtoken) +{ + return WriteIC(std::make_pair(DBKeys::TOKEN, wtoken.GetHash()), SER_INFO(wtoken)); +} + +bool WalletBatch::EraseToken(uint256 hash) +{ + return EraseIC(std::make_pair(DBKeys::TOKEN, hash)); +} + +bool WalletBatch::WriteTokenTx(const CTokenTx &wTokenTx) +{ + return WriteIC(std::make_pair(DBKeys::TOKENTX, wTokenTx.GetHash()), SER_INFO(wTokenTx)); +} + +bool WalletBatch::EraseTokenTx(uint256 hash) +{ + return EraseIC(std::make_pair(DBKeys::TOKENTX, hash)); +} + +bool WalletBatch::WriteContractData(const std::string &address, const std::string &key, const std::string &value) +{ + return WriteIC(std::make_pair(DBKeys::CONTRACTDATA, std::make_pair(address, key)), value); +} + +bool WalletBatch::EraseContractData(const std::string &address, const std::string &key) +{ + return EraseIC(std::make_pair(DBKeys::CONTRACTDATA, std::make_pair(address, key))); +} + +bool WalletBatch::WriteDelegation(const CDelegationInfo &wdelegation) +{ + return WriteIC(std::make_pair(DBKeys::DELEGATION, wdelegation.GetHash()), SER_INFO(wdelegation)); +} + +bool WalletBatch::EraseDelegation(uint256 hash) +{ + return EraseIC(std::make_pair(DBKeys::DELEGATION, hash)); +} + +bool WalletBatch::WriteSuperStaker(const CSuperStakerInfo &wsuperStaker) +{ + return WriteIC(std::make_pair(DBKeys::SUPERSTAKER, wsuperStaker.GetHash()), SER_INFO(wsuperStaker)); +} + +bool WalletBatch::EraseSuperStaker(uint256 hash) +{ + return EraseIC(std::make_pair(DBKeys::SUPERSTAKER, hash)); +} + std::unique_ptr MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error) { bool exists; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 9474a59660..265c0a59ff 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -26,6 +26,10 @@ class CMasterKey; class CWallet; class CWalletTx; struct WalletContext; +class CTokenInfo; +class CTokenTx; +class CDelegationInfo; +class CSuperStakerInfo; /** * Overview of wallet database classes: @@ -88,6 +92,11 @@ extern const std::string WALLETDESCRIPTORCKEY; extern const std::string WALLETDESCRIPTORKEY; extern const std::string WATCHMETA; extern const std::string WATCHS; +extern const std::string TOKEN; +extern const std::string TOKENTX; +extern const std::string CONTRACTDATA; +extern const std::string DELEGATION; +extern const std::string SUPERSTAKER; // Keys in this set pertain only to the legacy wallet (LegacyScriptPubKeyMan) and are removed during migration from legacy to descriptors. extern const std::unordered_set LEGACY_TYPES; @@ -234,6 +243,18 @@ class WalletBatch bool WriteTx(const CWalletTx& wtx); bool EraseTx(uint256 hash); + bool WriteToken(const CTokenInfo& wtoken); + bool EraseToken(uint256 hash); + + bool WriteTokenTx(const CTokenTx& wTokenTx); + bool EraseTokenTx(uint256 hash); + + bool WriteDelegation(const CDelegationInfo& wdelegation); + bool EraseDelegation(uint256 hash); + + bool WriteSuperStaker(const CSuperStakerInfo& wsuperStaker); + bool EraseSuperStaker(uint256 hash); + bool WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite); bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata &keyMeta); bool WriteCryptedKey(const CPubKey& vchPubKey, const std::vector& vchCryptedSecret, const CKeyMetadata &keyMeta); @@ -274,6 +295,11 @@ class WalletBatch bool WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bool internal); bool EraseActiveScriptPubKeyMan(uint8_t type, bool internal); + /// Write contract data key,value tuple to database + bool WriteContractData(const std::string &address, const std::string &key, const std::string &value); + /// Erase contract data tuple from wallet database + bool EraseContractData(const std::string &address, const std::string &key); + DBErrors LoadWallet(CWallet* pwallet); //! write the hdchain model (external chain child index counter) @@ -315,6 +341,11 @@ bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::stri bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); bool LoadEncryptionKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr); +bool LoadToken(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); +bool LoadTokenTx(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); +bool LoadDelegation(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); +bool LoadSuperStaker(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); +bool LoadContractData(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); } // namespace wallet #endif // BITCOIN_WALLET_WALLETDB_H