Wallet refactor
Signed-off-by: Anthony Fieroni <bvbfan@abv.bg>
This commit is contained in:
parent
87dfdabf94
commit
eaaba6880d
10 changed files with 98 additions and 57 deletions
|
@ -167,8 +167,8 @@ public:
|
|||
void disconnect() override
|
||||
{
|
||||
if (m_notifications) {
|
||||
m_notifications = nullptr;
|
||||
UnregisterValidationInterface(this);
|
||||
m_notifications = nullptr;
|
||||
}
|
||||
}
|
||||
void TransactionAddedToMempool(const CTransactionRef& tx) override
|
||||
|
@ -347,15 +347,17 @@ public:
|
|||
{
|
||||
return MakeUnique<NotificationsHandlerImpl>(*this, notifications);
|
||||
}
|
||||
void waitForNotificationsIfNewBlocksConnected(const uint256& old_tip) override
|
||||
bool waitForNotificationsIfTipIsNotSame(const uint256& tip) override
|
||||
{
|
||||
if (!old_tip.IsNull()) {
|
||||
if (!tip.IsNull()) {
|
||||
LOCK(::cs_main);
|
||||
if (old_tip == ::ChainActive().Tip()->GetBlockHash()) return;
|
||||
CBlockIndex* block = LookupBlockIndex(old_tip);
|
||||
if (block && block->GetAncestor(::ChainActive().Height()) == ::ChainActive().Tip()) return;
|
||||
auto chainTip = ::ChainActive().Tip();
|
||||
if (tip == chainTip->GetBlockHash()) return true;
|
||||
CBlockIndex* block = LookupBlockIndex(tip);
|
||||
if (block && block->GetAncestor(::ChainActive().Height()) == chainTip) return true;
|
||||
}
|
||||
SyncWithValidationInterfaceQueue();
|
||||
return false;
|
||||
}
|
||||
std::unique_ptr<Handler> handleRpc(const CRPCCommand& command) override
|
||||
{
|
||||
|
|
|
@ -230,7 +230,7 @@ public:
|
|||
//! Wait for pending notifications to be processed unless block hash points to the current
|
||||
//! chain tip, or to a possible descendant of the current chain tip that isn't currently
|
||||
//! connected.
|
||||
virtual void waitForNotificationsIfNewBlocksConnected(const uint256& old_tip) = 0;
|
||||
virtual bool waitForNotificationsIfTipIsNotSame(const uint256& tip) = 0;
|
||||
|
||||
//! Register handler for RPC. Command is not copied, so reference
|
||||
//! needs to remain valid until Handler is disconnected.
|
||||
|
|
|
@ -197,12 +197,16 @@ void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> fun
|
|||
}
|
||||
|
||||
void SingleThreadedSchedulerClient::EmptyQueue() {
|
||||
assert(!m_pscheduler->AreThreadsServicingQueue());
|
||||
bool should_continue = true;
|
||||
if (!m_pscheduler->AreThreadsServicingQueue())
|
||||
return;
|
||||
auto pendingCallbacks = [this]() -> bool {
|
||||
LOCK(m_cs_callbacks_pending);
|
||||
return !m_callbacks_pending.empty();
|
||||
};
|
||||
bool should_continue = pendingCallbacks();
|
||||
while (should_continue) {
|
||||
ProcessQueue();
|
||||
LOCK(m_cs_callbacks_pending);
|
||||
should_continue = !m_callbacks_pending.empty();
|
||||
should_continue = pendingCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
|
|||
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
||||
if (g_signals.m_internals) {
|
||||
g_signals.m_internals->m_connMainSignals.erase(pwalletIn);
|
||||
g_signals.FlushBackgroundCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -326,6 +326,9 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
|
|||
if (nValue <= 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
|
||||
|
||||
if (nValue > curBalance)
|
||||
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
|
||||
|
||||
// Parse Bitcoin address
|
||||
const CScript scriptPubKey = prefix + GetScriptForDestination(address);
|
||||
|
||||
|
|
|
@ -107,6 +107,15 @@ UniValue LookupAllNames() {
|
|||
return rpc_method(req);
|
||||
}
|
||||
|
||||
UniValue PruneAbandonFunds(const uint256& txid) {
|
||||
auto rpc_method = tableRPC["removeprunedfunds"];
|
||||
JSONRPCRequest req;
|
||||
req.URI = "/wallet/tester_wallet";
|
||||
req.params = UniValue(UniValue::VARR);
|
||||
req.params.push_back(txid.GetHex());
|
||||
return rpc_method(req);
|
||||
}
|
||||
|
||||
std::vector<uint256> generateBlock(int blocks = 1) {
|
||||
auto rpc_method = tableRPC["generatetoaddress"];
|
||||
JSONRPCRequest req;
|
||||
|
@ -505,5 +514,17 @@ BOOST_AUTO_TEST_CASE(can_claim_after_each_fork)
|
|||
BOOST_CHECK_EQUAL(looked.size(), 4U);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(remove_pruned_funds)
|
||||
{
|
||||
generateBlock(140);
|
||||
// claim a name
|
||||
auto txid = ClaimAName("tester", "deadbeef", "1.0");
|
||||
generateBlock();
|
||||
// abandon claim
|
||||
AbandonAClaim(txid);
|
||||
generateBlock();
|
||||
PruneAbandonFunds(txid);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ bool AddWallet(const std::shared_ptr<CWallet>& wallet)
|
|||
{
|
||||
LOCK(cs_wallets);
|
||||
assert(wallet);
|
||||
std::vector<std::shared_ptr<CWallet>>::const_iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet);
|
||||
auto i = std::find(vpwallets.begin(), vpwallets.end(), wallet);
|
||||
if (i != vpwallets.end()) return false;
|
||||
vpwallets.push_back(wallet);
|
||||
return true;
|
||||
|
@ -107,13 +107,9 @@ static std::set<std::string> g_unloading_wallet_set;
|
|||
// Custom deleter for shared_ptr<CWallet>.
|
||||
static void ReleaseWallet(CWallet* wallet)
|
||||
{
|
||||
// Unregister and delete the wallet right after BlockUntilSyncedToCurrentChain
|
||||
// so that it's in sync with the current chainstate.
|
||||
const std::string name = wallet->GetName();
|
||||
wallet->WalletLogPrintf("Releasing wallet\n");
|
||||
wallet->BlockUntilSyncedToCurrentChain();
|
||||
wallet->Flush();
|
||||
wallet->m_chain_notifications_handler.reset();
|
||||
delete wallet;
|
||||
// Wallet is now released, notify UnloadWallet, if any.
|
||||
{
|
||||
|
@ -139,6 +135,8 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
|
|||
// Notify the unload intent so that all remaining shared pointers are
|
||||
// released.
|
||||
wallet->NotifyUnload();
|
||||
wallet->BlockUntilSyncedToCurrentChain();
|
||||
wallet->m_chain_notifications_handler.reset();
|
||||
// Time to ditch our shared_ptr and wait for ReleaseWallet call.
|
||||
wallet.reset();
|
||||
{
|
||||
|
@ -396,8 +394,8 @@ bool CWallet::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const C
|
|||
|
||||
if (!IsCrypted()) {
|
||||
return batch.WriteKey(pubkey,
|
||||
secret.GetPrivKey(),
|
||||
mapKeyMetadata[pubkey.GetID()]);
|
||||
secret.GetPrivKey(),
|
||||
mapKeyMetadata[pubkey.GetID()]);
|
||||
}
|
||||
UnsetWalletFlagWithDB(batch, WALLET_FLAG_BLANK_WALLET);
|
||||
return true;
|
||||
|
@ -418,12 +416,12 @@ bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
|
|||
LOCK(cs_wallet);
|
||||
if (encrypted_batch)
|
||||
return encrypted_batch->WriteCryptedKey(vchPubKey,
|
||||
vchCryptedSecret,
|
||||
mapKeyMetadata[vchPubKey.GetID()]);
|
||||
vchCryptedSecret,
|
||||
mapKeyMetadata[vchPubKey.GetID()]);
|
||||
else
|
||||
return WalletBatch(*database).WriteCryptedKey(vchPubKey,
|
||||
vchCryptedSecret,
|
||||
mapKeyMetadata[vchPubKey.GetID()]);
|
||||
vchCryptedSecret,
|
||||
mapKeyMetadata[vchPubKey.GetID()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -742,8 +740,6 @@ std::set<uint256> CWallet::GetConflicts(const uint256& txid) const
|
|||
return result;
|
||||
const CWalletTx& wtx = it->second;
|
||||
|
||||
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
|
||||
|
||||
for (const CTxIn& txin : wtx.tx->vin)
|
||||
{
|
||||
auto hitTx = mapTxSpends.find(txin.prevout.hash);
|
||||
|
@ -792,10 +788,11 @@ void CWallet::SyncMetaData(const COutPoint& outPoint)
|
|||
auto hitN = hitTx->second.find(outPoint.n);
|
||||
if (hitN != hitTx->second.end()) {
|
||||
for (auto &spend: hitN->second) {
|
||||
const CWalletTx *wtx = &mapWallet.at(spend);
|
||||
if (wtx->nOrderPos < nMinOrderPos) {
|
||||
nMinOrderPos = wtx->nOrderPos;
|
||||
copyFrom = wtx;
|
||||
if (auto wtx = GetWalletTx(spend)) {
|
||||
if (wtx->nOrderPos < nMinOrderPos) {
|
||||
nMinOrderPos = wtx->nOrderPos;
|
||||
copyFrom = wtx;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!copyFrom) {
|
||||
|
@ -804,7 +801,9 @@ void CWallet::SyncMetaData(const COutPoint& outPoint)
|
|||
|
||||
// Now copy data from copyFrom to rest:
|
||||
for (auto &hash: hitN->second) {
|
||||
CWalletTx *copyTo = &mapWallet.at(hash);
|
||||
auto it = mapWallet.find(hash);
|
||||
if (it != mapWallet.end()) continue;
|
||||
CWalletTx* copyTo = &it->second;
|
||||
if (copyFrom == copyTo) continue;
|
||||
assert(copyFrom && "Oldest wallet transaction in range assumed to have been found.");
|
||||
if (!copyFrom->IsEquivalentTo(*copyTo)) continue;
|
||||
|
@ -853,16 +852,13 @@ void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)
|
|||
}
|
||||
|
||||
|
||||
void CWallet::AddToSpends(const uint256& wtxid)
|
||||
void CWallet::AddToSpends(const CWalletTx& wtx)
|
||||
{
|
||||
auto it = mapWallet.find(wtxid);
|
||||
assert(it != mapWallet.end());
|
||||
CWalletTx& thisTx = it->second;
|
||||
if (thisTx.IsCoinBase()) // Coinbases don't spend anything!
|
||||
if (wtx.IsCoinBase()) // Coinbases don't spend anything!
|
||||
return;
|
||||
|
||||
for (const CTxIn& txin : thisTx.tx->vin)
|
||||
AddToSpends(txin.prevout, wtxid);
|
||||
for (const CTxIn& txin : wtx.tx->vin)
|
||||
AddToSpends(txin.prevout, wtx.GetHash());
|
||||
}
|
||||
|
||||
bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
||||
|
@ -1121,7 +1117,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
|
|||
|
||||
WalletBatch batch(*database, "r+", fFlushOnClose);
|
||||
|
||||
uint256 hash = wtxIn.GetHash();
|
||||
const uint256& hash = wtxIn.GetHash();
|
||||
|
||||
if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) {
|
||||
// Mark used destinations
|
||||
|
@ -1145,7 +1141,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
|
|||
wtx.nOrderPos = IncOrderPosNext(&batch);
|
||||
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
|
||||
wtx.nTimeSmart = ComputeTimeSmart(wtx);
|
||||
AddToSpends(hash);
|
||||
AddToSpends(wtx);
|
||||
}
|
||||
|
||||
bool fUpdated = false;
|
||||
|
@ -1219,14 +1215,14 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn)
|
|||
wtxIn.m_confirm.nIndex = 0;
|
||||
}
|
||||
}
|
||||
uint256 hash = wtxIn.GetHash();
|
||||
const uint256& hash = wtxIn.GetHash();
|
||||
const auto& ins = mapWallet.emplace(hash, wtxIn);
|
||||
CWalletTx& wtx = ins.first->second;
|
||||
wtx.BindWallet(this);
|
||||
if (/* insertion took place */ ins.second) {
|
||||
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
|
||||
}
|
||||
AddToSpends(hash);
|
||||
AddToSpends(wtx);
|
||||
for (const CTxIn& txin : wtx.tx->vin) {
|
||||
auto it = mapWallet.find(txin.prevout.hash);
|
||||
if (it != mapWallet.end()) {
|
||||
|
@ -1502,8 +1498,13 @@ void CWallet::BlockUntilSyncedToCurrentChain() {
|
|||
// ::ChainActive().Tip(), otherwise put a callback in the validation interface queue and wait
|
||||
// for the queue to drain enough to execute it (indicating we are caught up
|
||||
// at least with the time we entered this function).
|
||||
uint256 last_block_hash = WITH_LOCK(cs_wallet, return m_last_block_processed);
|
||||
chain().waitForNotificationsIfNewBlocksConnected(last_block_hash);
|
||||
auto lastProcessedBlock = [this]() -> uint256 {
|
||||
return WITH_LOCK(cs_wallet, return m_last_block_processed);
|
||||
};
|
||||
bool tipIsSame = false;
|
||||
uint256 last_block_hash;
|
||||
while (!tipIsSame && !(last_block_hash = lastProcessedBlock()).IsNull())
|
||||
tipIsSame = chain().waitForNotificationsIfTipIsNotSame(last_block_hash);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2242,7 +2243,7 @@ std::set<uint256> CWalletTx::GetConflicts() const
|
|||
std::set<uint256> result;
|
||||
if (pwallet != nullptr)
|
||||
{
|
||||
uint256 myHash = GetHash();
|
||||
const uint256& myHash = GetHash();
|
||||
result = pwallet->GetConflicts(myHash);
|
||||
result.erase(myHash);
|
||||
}
|
||||
|
@ -2317,7 +2318,7 @@ CAmount CWalletTx::GetAvailableCredit(interfaces::Chain::Lock& locked_chain, boo
|
|||
|
||||
bool allow_used_addresses = (filter & ISMINE_USED) || !pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
|
||||
CAmount nCredit = 0;
|
||||
uint256 hashTx = GetHash();
|
||||
const uint256& hashTx = GetHash();
|
||||
for (unsigned int i = 0; i < tx->vout.size(); i++)
|
||||
{
|
||||
if (!pwallet->IsSpent(locked_chain, hashTx, i) && (allow_used_addresses || !pwallet->IsUsedDestination(hashTx, i))) {
|
||||
|
@ -3450,9 +3451,18 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
|
|||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
DBErrors nZapSelectTxRet = WalletBatch(*database, "cr+").ZapSelectTx(vHashIn, vHashOut);
|
||||
for (uint256 hash : vHashOut) {
|
||||
const auto& it = mapWallet.find(hash);
|
||||
for (const uint256& hash : vHashOut) {
|
||||
auto it = mapWallet.find(hash);
|
||||
if (it == mapWallet.end())
|
||||
continue;
|
||||
wtxOrdered.erase(it->second.m_it_wtxOrdered);
|
||||
for (auto& txin : it->second.tx->vin) {
|
||||
auto mit = mapTxSpends.find(txin.prevout.hash);
|
||||
if (mit != mapTxSpends.end()) {
|
||||
if (mit->second.erase(txin.prevout.n) && mit->second.empty())
|
||||
mapTxSpends.erase(mit);
|
||||
}
|
||||
}
|
||||
mapWallet.erase(it);
|
||||
NotifyTransactionChanged(this, hash, CT_DELETED);
|
||||
}
|
||||
|
@ -4096,7 +4106,7 @@ void CWallet::UnlockAllCoins()
|
|||
setLockedCoins.clear();
|
||||
}
|
||||
|
||||
bool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const
|
||||
bool CWallet::IsLockedCoin(const uint256& hash, unsigned int n) const
|
||||
{
|
||||
AssertLockHeld(cs_wallet);
|
||||
COutPoint outpt(hash, n);
|
||||
|
@ -4662,7 +4672,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
|
|||
|
||||
for (const CWalletTx& wtxOld : vWtx)
|
||||
{
|
||||
uint256 hash = wtxOld.GetHash();
|
||||
const uint256& hash = wtxOld.GetHash();
|
||||
auto mi = walletInstance->mapWallet.find(hash);
|
||||
if (mi != walletInstance->mapWallet.end())
|
||||
{
|
||||
|
|
|
@ -789,7 +789,7 @@ private:
|
|||
typedef robin_hood::unordered_map<uint256, robin_hood::unordered_map<uint32_t, std::vector<uint256>>> TxSpends;
|
||||
TxSpends mapTxSpends GUARDED_BY(cs_wallet);
|
||||
void AddToSpends(const COutPoint& outpoint, const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void AddToSpends(const uint256& wtxid) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void AddToSpends(const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
||||
/**
|
||||
* Add a transaction to the wallet, or update it. pIndex and posInBlock should
|
||||
|
@ -1012,7 +1012,7 @@ public:
|
|||
|
||||
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;
|
||||
|
||||
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
bool IsLockedCoin(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void UnlockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
void UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
|
||||
|
@ -1406,8 +1406,8 @@ public:
|
|||
|
||||
/** Returns a bracketed wallet name for displaying in logs, will return [default wallet] if the wallet has no name */
|
||||
const std::string GetDisplayName() const {
|
||||
std::string wallet_name = GetName().length() == 0 ? "default wallet" : GetName();
|
||||
return strprintf("[%s]", wallet_name);
|
||||
const std::string wallet_name = GetName();
|
||||
return strprintf("[%s]", wallet_name.empty() ? "default wallet" : wallet_name);
|
||||
};
|
||||
|
||||
/** Prepends the wallet name in logging output to ease debugging in multi-wallet use cases */
|
||||
|
|
|
@ -78,7 +78,7 @@ bool WalletBatch::WriteTx(const CWalletTx& wtx)
|
|||
return WriteIC(std::make_pair(DBKeys::TX, wtx.GetHash()), wtx);
|
||||
}
|
||||
|
||||
bool WalletBatch::EraseTx(uint256 hash)
|
||||
bool WalletBatch::EraseTx(const uint256& hash)
|
||||
{
|
||||
return EraseIC(std::make_pair(DBKeys::TX, hash));
|
||||
}
|
||||
|
@ -615,10 +615,10 @@ DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<u
|
|||
|
||||
// erase each matching wallet TX
|
||||
bool delerror = false;
|
||||
std::vector<uint256>::iterator it = vTxHashIn.begin();
|
||||
auto it = vTxHashIn.begin();
|
||||
for (const uint256& hash : vTxHash) {
|
||||
while (it < vTxHashIn.end() && (*it) < hash) {
|
||||
it++;
|
||||
while (it != vTxHashIn.end() && (*it) < hash) {
|
||||
++it;
|
||||
}
|
||||
if (it == vTxHashIn.end()) {
|
||||
break;
|
||||
|
|
|
@ -220,7 +220,7 @@ public:
|
|||
bool ErasePurpose(const std::string& strAddress);
|
||||
|
||||
bool WriteTx(const CWalletTx& wtx);
|
||||
bool EraseTx(uint256 hash);
|
||||
bool EraseTx(const uint256& hash);
|
||||
|
||||
bool WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite);
|
||||
bool WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata &keyMeta);
|
||||
|
|
Loading…
Add table
Reference in a new issue