3

I'm interested in implementing a view wallet in code. I've seen this example which worked great, however the only problem is it also requires a public spend key.

I saw this question but I can't really follow what steps I would have to take to extract all that data from a transaction.

Can somone layout in psuedo or actual code the process needed to tell if an output belongs to you in a given transaction? Here's what I have so far, adapted from the github example:

void printTxOutputs(Crypto::Hash transactionHash)
{
  std::vector<TransactionDetails> transactions;
  std::vector<Crypto::Hash> transactionHashes;
  transactionHashes.push_back(transactionHash);

  m_node->getTransactions(transactionHashes, transactions, callback);

  TransactionDetails ourTransaction = transactions[0];

  AccountKeys keys;
  m_wallet->getAccountKeys(keys);

  Crypto::SecretKey privateViewKey = keys.viewSecretKey;
  Crypto::SecretKey privateSpendKey = keys.spendSecretKey;

  /* The transaction public key */
  Crypto::PublicKey publicTransactionKey = ourTransaction.extra.publicKey;

  /* The users public spend key */
  Crypto::PublicKey publicSpendKey;
  Crypto::secret_key_to_public_key(privateSpendKey, publicSpendKey);

  /* Derived key is created from the users private view key and the public
     transaction key */
  Crypto::KeyDerivation derivation;
  Crypto::generate_key_derivation(publicTransactionKey, privateViewKey, derivation);

  Crypto::PublicKey outputPublicKey;

  bool found = false;
  uint64_t sum = 0;

  /* Loop through each output in the transaction */
  for (size_t i = 0; i < ourTransaction.outputs.size(); ++i)
  {
    Crypto::derive_public_key(derivation, i, publicSpendKey, outputPublicKey);

    TransactionOutputTarget target = ourTransaction.outputs[i].output.target;

    KeyOutput targetPubKey = boost::get<CryptoNote::KeyOutput>(target);

    if (targetPubKey.key == outputPublicKey)
    {
      uint64_t amount = ourTransaction.outputs[i].output.amount; 

      std::string xmr = m_currency.formatAmount(amount);

      sum += amount;

      found = true;

      logger(INFO, GREEN) << "The transaction output of " << xmr << " XMR belongs to you!";
    }
  }

  if (!found)
  {
    logger(ERROR, BRIGHT_RED) << "No outputs were found that belong to you, searched " << ourTransaction.outputs.size() << " outputs.";
  }
  else
  {
    std::string xmr = m_currency.formatAmount(sum);

    logger(INFO, GREEN) << "Outputs totalling " << xmr << " XMR were sent to your wallet!";
  }
}

The code is slightly pruned to remove cruft.

knaccc
  • 8,518
  • 17
  • 23
Zpalmtree
  • 133
  • 3

1 Answers1

2

Your code looks fine to me. Here is an explanation of what it's doing:

  1. For each transaction, get the public key of each output listed in the transaction. Call the public key P.

  2. We need to check whether this output was destined for us. We do this by calculating P' as P' = Hs(aR || i)G + B. If P' == P then the output is destined for us.

The generate_key_derivation method in your code produces aR. The derive_public_key uses that to then calculate P' as Hs(aR || i)G + B.

The only step that I think seems to be missing is to decrypt the amount of the output. For details, see my answer here How the receiver knows the amount that he received from the sender?

You probably also want to extract the public spend key from the user's public wallet address, rather than require that you know their private spend key to derive it. For information on how to extract it, see https://xmr.llcoins.net/addresstests.html

knaccc
  • 8,518
  • 17
  • 23