Skip to content
This repository has been archived by the owner on Apr 16, 2023. It is now read-only.

Commit

Permalink
Don't regenerate key images where not necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
zpalmtree committed Nov 18, 2019
1 parent 568ee8c commit 058aa37
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 77 deletions.
80 changes: 60 additions & 20 deletions include/WalletTypes.h
Expand Up @@ -139,6 +139,9 @@ namespace WalletTypes
/* The transaction hash of the transaction that contains this input */
Crypto::Hash parentTransactionHash;

/* The private ephemeral generated along with the key image */
std::optional<Crypto::SecretKey> privateEphemeral;

bool operator==(const TransactionInput &other)
{
return keyImage == other.keyImage;
Expand All @@ -148,26 +151,43 @@ namespace WalletTypes
void toJSON(rapidjson::Writer<rapidjson::StringBuffer> &writer) const
{
writer.StartObject();
writer.Key("keyImage");
keyImage.toJSON(writer);
writer.Key("amount");
writer.Uint64(amount);
writer.Key("blockHeight");
writer.Uint64(blockHeight);
writer.Key("transactionPublicKey");
transactionPublicKey.toJSON(writer);
writer.Key("transactionIndex");
writer.Uint64(transactionIndex);
writer.Key("globalOutputIndex");
writer.Uint64(globalOutputIndex.value_or(0));
writer.Key("key");
key.toJSON(writer);
writer.Key("spendHeight");
writer.Uint64(spendHeight);
writer.Key("unlockTime");
writer.Uint64(unlockTime);
writer.Key("parentTransactionHash");
parentTransactionHash.toJSON(writer);
{
writer.Key("keyImage");
keyImage.toJSON(writer);

writer.Key("amount");
writer.Uint64(amount);

writer.Key("blockHeight");
writer.Uint64(blockHeight);

writer.Key("transactionPublicKey");
transactionPublicKey.toJSON(writer);

writer.Key("transactionIndex");
writer.Uint64(transactionIndex);

writer.Key("globalOutputIndex");
writer.Uint64(globalOutputIndex.value_or(0));

writer.Key("key");
key.toJSON(writer);

writer.Key("spendHeight");
writer.Uint64(spendHeight);

writer.Key("unlockTime");
writer.Uint64(unlockTime);

writer.Key("parentTransactionHash");
parentTransactionHash.toJSON(writer);

if (privateEphemeral)
{
writer.Key("privateEphemeral");
privateEphemeral->toJSON(writer);
}
}
writer.EndObject();
}

Expand All @@ -184,6 +204,13 @@ namespace WalletTypes
spendHeight = getUint64FromJSON(j, "spendHeight");
unlockTime = getUint64FromJSON(j, "unlockTime");
parentTransactionHash.fromString(getStringFromJSON(j, "parentTransactionHash"));

if (j.HasMember("privateEphemeral"))
{
Crypto::SecretKey tmp;
tmp.fromString(getStringFromJSON(j, "privateEphemeral"));
privateEphemeral = tmp;
}
}
};

Expand Down Expand Up @@ -217,26 +244,39 @@ namespace WalletTypes
/* The amount of the transaction output */
uint64_t amount;
};

struct GlobalIndexKey
{
uint64_t index;
Crypto::PublicKey key;
};

struct ObscuredInput
{
/* The outputs, including our real output, and the fake mixin outputs */
std::vector<GlobalIndexKey> outputs;

/* The index of the real output in the outputs vector */
uint64_t realOutput;

/* The real transaction public key */
Crypto::PublicKey realTransactionPublicKey;

/* The index in the transaction outputs vector */
uint64_t realOutputTransactionIndex;

/* The amount being sent */
uint64_t amount;

/* The owners keys, so we can sign the input correctly */
Crypto::PublicKey ownerPublicSpendKey;
Crypto::SecretKey ownerPrivateSpendKey;

/* The key image of the input */
Crypto::KeyImage keyImage;

/* The private ephemeral generated along with the key image */
std::optional<Crypto::SecretKey> privateEphemeral;
};

class Transaction
Expand Down
8 changes: 5 additions & 3 deletions src/subwallets/SubWallet.cpp
Expand Up @@ -52,7 +52,7 @@ SubWallet::SubWallet(
/* CLASS FUNCTIONS */
/////////////////////

Crypto::KeyImage SubWallet::getTxInputKeyImage(
std::tuple<Crypto::KeyImage, Crypto::SecretKey> SubWallet::getTxInputKeyImage(
const Crypto::KeyDerivation derivation,
const size_t outputIndex,
const bool isViewWallet) const
Expand All @@ -76,9 +76,11 @@ Crypto::KeyImage SubWallet::getTxInputKeyImage(

/* Get the key image from the tmp public and private key */
Crypto::generate_key_image(tmp.publicKey, tmp.secretKey, keyImage);
return keyImage;

return { keyImage, tmp.secretKey };
}
return Crypto::KeyImage();

return { Crypto::KeyImage(), Crypto::SecretKey() };
}

void SubWallet::storeTransactionInput(const WalletTypes::TransactionInput input, const bool isViewWallet)
Expand Down
2 changes: 1 addition & 1 deletion src/subwallets/SubWallet.h
Expand Up @@ -49,7 +49,7 @@ class SubWallet

/* Generates a key image from the derivation, and stores the
transaction input along with the key image filled in */
Crypto::KeyImage getTxInputKeyImage(
std::tuple<Crypto::KeyImage, Crypto::SecretKey> getTxInputKeyImage(
const Crypto::KeyDerivation derivation,
const size_t outputIndex,
const bool isViewWallet) const;
Expand Down
2 changes: 1 addition & 1 deletion src/subwallets/SubWallets.cpp
Expand Up @@ -364,7 +364,7 @@ void SubWallets::addTransaction(const WalletTypes::Transaction tx)
m_transactions.push_back(tx);
}

Crypto::KeyImage SubWallets::getTxInputKeyImage(
std::tuple<Crypto::KeyImage, Crypto::SecretKey> SubWallets::getTxInputKeyImage(
const Crypto::PublicKey publicSpendKey,
const Crypto::KeyDerivation derivation,
const size_t outputIndex) const
Expand Down
2 changes: 1 addition & 1 deletion src/subwallets/SubWallets.h
Expand Up @@ -69,7 +69,7 @@ class SubWallets
/* Generates a key image using the public+private spend key of the
subwallet. Will return an uninitialized keyimage if a view wallet
(and must exist, but the WalletSynchronizer already checks this) */
Crypto::KeyImage getTxInputKeyImage(
std::tuple<Crypto::KeyImage, Crypto::SecretKey> getTxInputKeyImage(
const Crypto::PublicKey publicSpendKey,
const Crypto::KeyDerivation derivation,
const size_t outputIndex) const;
Expand Down
76 changes: 40 additions & 36 deletions src/walletbackend/Transfer.cpp
Expand Up @@ -10,6 +10,7 @@
#include <config/WalletConfig.h>
#include <common/Varint.h>
#include <errors/ValidateParameters.h>
#include <logger/Logger.h>
#include <utilities/Addresses.h>
#include <utilities/FormatTools.h>
#include <utilities/Mixins.h>
Expand Down Expand Up @@ -648,6 +649,10 @@ namespace SendTransaction

obscuredInput.ownerPrivateSpendKey = walletAmount.privateSpendKey;

obscuredInput.keyImage = walletAmount.input.keyImage;

obscuredInput.privateEphemeral = walletAmount.input.privateEphemeral;

if (mixin != 0)
{
/* Add the fake outputs to the transaction */
Expand Down Expand Up @@ -706,33 +711,6 @@ namespace SendTransaction
return {SUCCESS, result};
}

std::tuple<CryptoNote::KeyPair, Crypto::KeyImage>
genKeyImage(const WalletTypes::ObscuredInput input, const Crypto::SecretKey privateViewKey)
{
Crypto::KeyDerivation derivation;

/* Derive the key from the transaction public key, and our private
view key */
Crypto::generate_key_derivation(input.realTransactionPublicKey, privateViewKey, derivation);

CryptoNote::KeyPair tmpKeyPair;

/* Derive the public key of the tmp key pair */
Crypto::derive_public_key(
derivation, input.realOutputTransactionIndex, input.ownerPublicSpendKey, tmpKeyPair.publicKey);

/* Derive the secret key of the tmp key pair */
Crypto::derive_secret_key(
derivation, input.realOutputTransactionIndex, input.ownerPrivateSpendKey, tmpKeyPair.secretKey);

Crypto::KeyImage keyImage;

/* Generate the key image */
Crypto::generate_key_image(tmpKeyPair.publicKey, tmpKeyPair.secretKey, keyImage);

return {tmpKeyPair, keyImage};
}

std::tuple<Error, std::vector<CryptoNote::KeyInput>, std::vector<Crypto::SecretKey>> setupInputs(
const std::vector<WalletTypes::ObscuredInput> inputsAndFakes,
const Crypto::SecretKey privateViewKey)
Expand All @@ -741,21 +719,40 @@ namespace SendTransaction

std::vector<Crypto::SecretKey> tmpSecretKeys;

for (const auto input : inputsAndFakes)
int numPregenerated = 0;
int numGeneratedOnDemand = 0;

for (auto input : inputsAndFakes)
{
const auto [tmpKeyPair, keyImage] = genKeyImage(input, privateViewKey);
CryptoNote::KeyInput keyInput;

keyInput.amount = input.amount;
keyInput.keyImage = input.keyImage;

if (tmpKeyPair.publicKey != input.outputs[input.realOutput].key)
if (!input.privateEphemeral)
{
return {INVALID_GENERATED_KEYIMAGE, inputs, tmpSecretKeys};
}
Crypto::KeyDerivation derivation;

tmpSecretKeys.push_back(tmpKeyPair.secretKey);
/* Derive the key from the transaction public key, and our private
view key */
Crypto::generate_key_derivation(input.realTransactionPublicKey, privateViewKey, derivation);

CryptoNote::KeyInput keyInput;
Crypto::SecretKey privateEphemeral;

keyInput.amount = input.amount;
keyInput.keyImage = keyImage;
/* Derive the privateEphemeral */
Crypto::derive_secret_key(
derivation, input.realOutputTransactionIndex, input.ownerPrivateSpendKey, privateEphemeral);

input.privateEphemeral = privateEphemeral;

numGeneratedOnDemand++;
}
else
{
numPregenerated++;
}

tmpSecretKeys.push_back(*input.privateEphemeral);

/* Add each output index from the fake outs */
std::transform(
Expand Down Expand Up @@ -785,6 +782,13 @@ namespace SendTransaction
inputs.push_back(keyInput);
}

Logger::logger.log(
"Generated private ephemerals for " + std::to_string(numGeneratedOnDemand) + " inputs, "
"used pre-generated ephemerals for " + std::to_string(numPregenerated) + " inputs.",
Logger::DEBUG,
{ Logger::TRANSACTIONS }
);

return {SUCCESS, inputs, tmpSecretKeys};
}

Expand Down
3 changes: 0 additions & 3 deletions src/walletbackend/Transfer.h
Expand Up @@ -108,9 +108,6 @@ namespace SendTransaction
std::tuple<Error, Crypto::Hash>
relayTransaction(const CryptoNote::Transaction tx, const std::shared_ptr<Nigel> daemon);

std::tuple<CryptoNote::KeyPair, Crypto::KeyImage>
genKeyImage(const WalletTypes::ObscuredInput input, const Crypto::SecretKey privateViewKey);

void storeSentTransaction(
const Crypto::Hash hash,
const uint64_t fee,
Expand Down
27 changes: 15 additions & 12 deletions src/walletbackend/WalletSynchronizer.cpp
Expand Up @@ -556,21 +556,24 @@ std::vector<std::tuple<Crypto::PublicKey, WalletTypes::TransactionInput>> Wallet
we'll let the subwallet do this since we need the private spend
key. We use the key images to detect outgoing transactions,
and we use the transaction inputs to make transactions ourself */
const Crypto::KeyImage keyImage =
m_subWallets->getTxInputKeyImage(derivedSpendKey, derivation, outputIndex);
const auto [keyImage, privateEphemeral]
= m_subWallets->getTxInputKeyImage(derivedSpendKey, derivation, outputIndex);

const uint64_t spendHeight = 0;

const WalletTypes::TransactionInput input({keyImage,
output.amount,
blockHeight,
rawTX.transactionPublicKey,
outputIndex,
output.globalOutputIndex,
output.key,
spendHeight,
rawTX.unlockTime,
rawTX.hash});
const WalletTypes::TransactionInput input({
keyImage,
output.amount,
blockHeight,
rawTX.transactionPublicKey,
outputIndex,
output.globalOutputIndex,
output.key,
spendHeight,
rawTX.unlockTime,
rawTX.hash,
privateEphemeral
});

inputs.emplace_back(derivedSpendKey, input);
}
Expand Down

0 comments on commit 058aa37

Please sign in to comment.