Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Massively speed up checking key image owner in WalletBackend #778

Merged
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.
+58 −43
Diff settings

Always

Just for now

@@ -171,35 +171,6 @@ std::string SubWallet::address() const
return m_address;
}

bool SubWallet::hasKeyImage(const Crypto::KeyImage keyImage) const
{
auto it = std::find_if(m_unspentInputs.begin(), m_unspentInputs.end(),
[&keyImage](const auto &input)
{
return input.keyImage == keyImage;
});

/* Found the key image */
if (it != m_unspentInputs.end())
{
return true;
}

/* Didn't find it in unlocked inputs, check the locked inputs */
it = std::find_if(m_lockedInputs.begin(), m_lockedInputs.end(),
[&keyImage](const auto &input)
{
return input.keyImage == keyImage;
});

return it != m_lockedInputs.end();

/* Note: We don't need to check the spent inputs - it should never show
up there, as the same key image can only be used once */

/* Also don't need to check unconfirmed inputs - we can't spend those yet */
}

Crypto::PublicKey SubWallet::publicSpendKey() const
{
return m_publicSpendKey;
@@ -440,6 +411,37 @@ void SubWallet::pruneSpentInputs(const uint64_t pruneHeight)
}
}

std::vector<Crypto::KeyImage> SubWallet::getKeyImages() const
{
std::vector<Crypto::KeyImage> result;

const auto getKeyImages = [&result](const auto &vec)
{
std::transform(vec.begin(), vec.end(), std::back_inserter(result), [](const auto &input)
{
return input.keyImage;
});
};

getKeyImages(m_unspentInputs);
getKeyImages(m_lockedInputs);
/* You may think we don't need to include the spent key images here, since
we're using this method to check if an transaction was sent by us
by comparing the key images, and a spent key image can of course not
be used more than once.
However, it is possible that a spent transaction gets orphaned, returns
to our wallet, and is then spent again. If we did not include the spent
key images, when we handle the fork and mark the inputs as unspent,
we would not know about the key images of those inputs.
Then, when we spend it again, we would not know it's our outgoing
transaction. */
getKeyImages(m_spentInputs);

return result;
}

void SubWallet::fromJSON(const JSONValue &j)
{
m_publicSpendKey.fromString(getStringFromJSON(j, "publicSpendKey"));
@@ -74,8 +74,6 @@ class SubWallet

std::string address() const;

bool hasKeyImage(const Crypto::KeyImage keyImage) const;

Crypto::PublicKey publicSpendKey() const;

Crypto::SecretKey privateSpendKey() const;
@@ -107,6 +105,8 @@ class SubWallet

void pruneSpentInputs(const uint64_t pruneHeight);

std::vector<Crypto::KeyImage> getKeyImages() const;

/////////////////////////////
/* Public member variables */
/////////////////////////////
@@ -411,6 +411,13 @@ void SubWallets::storeTransactionInput(
/* Check it exists */
if (it != m_subWallets.end())
{
if (!m_isViewWallet)
{
/* Add the new key image to the store, so we can detect when we
spent a key image easily */
m_keyImageOwners[input.keyImage] = publicSpendKey;
}

/* If we have a view wallet, don't attempt to derive the key image */
return it->second.storeTransactionInput(input, m_isViewWallet);
}
@@ -421,20 +428,11 @@ void SubWallets::storeTransactionInput(
std::tuple<bool, Crypto::PublicKey>
SubWallets::getKeyImageOwner(const Crypto::KeyImage keyImage) const
{
/* View wallet can't generate key images */
if (m_isViewWallet)
{
return {false, Crypto::PublicKey()};
}

std::scoped_lock lock(m_mutex);
const auto it = m_keyImageOwners.find(keyImage);

for (const auto & [publicKey, subWallet] : m_subWallets)
if (it != m_keyImageOwners.end())
{
if (subWallet.hasKeyImage(keyImage))
{
return {true, subWallet.publicSpendKey()};
}
return {true, it->second};
}

return {false, Crypto::PublicKey()};
@@ -983,6 +981,18 @@ void SubWallets::fromJSON(const JSONObject &j)
SubWallet s;
s.fromJSON(x);
m_subWallets[s.publicSpendKey()] = s;

/* Load the key images hashmap from the loaded subwallets */
if (!m_isViewWallet)
{
for (const auto &[pubKey, subWallet] : m_subWallets)
{
for (const auto &keyImage : subWallet.getKeyImages())
{
m_keyImageOwners[keyImage] = pubKey;
}
}
}
}

for (const auto &x : getArrayFromJSON(j, "transactions"))
@@ -232,6 +232,9 @@ class SubWallets
/* Transaction private keys of sent transactions, used for auditing */
std::unordered_map<Crypto::Hash, Crypto::SecretKey> m_transactionPrivateKeys;

/* A mapping of key images to the subwallet public spend key that owns them */
std::unordered_map<Crypto::KeyImage, Crypto::PublicKey> m_keyImageOwners;

/* Need a mutex for accessing inputs, transactions, and locked
transactions, etc as these are modified on multiple threads */
mutable std::mutex m_mutex;
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.