Skip to content
Permalink
Browse files

Don't regenerate key images where not necessary

  • Loading branch information
zpalmtree committed Nov 18, 2019
1 parent 568ee8c commit 058aa3708521bef776b49377f7dd0ab064a10dbf
@@ -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;
@@ -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();
}

@@ -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;
}
}
};

@@ -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
@@ -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
@@ -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)
@@ -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;
@@ -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
@@ -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;
@@ -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>
@@ -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 */
@@ -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)
@@ -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(
@@ -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};
}

@@ -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,
@@ -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);
}

0 comments on commit 058aa37

Please sign in to comment.
You can’t perform that action at this time.