// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "TransactionUtils.h" #include #include "crypto/crypto.h" #include "CryptoNoteCore/Account.h" #include "CryptoNoteFormatUtils.h" #include "TransactionExtra.h" using namespace Crypto; namespace CryptoNote { bool checkInputsKeyimagesDiff(const CryptoNote::TransactionPrefix& tx) { std::unordered_set ki; for (const auto& in : tx.inputs) { if (in.type() == typeid(KeyInput)) { if (!ki.insert(boost::get(in).keyImage).second) return false; } } return true; } // TransactionInput helper functions size_t getRequiredSignaturesCount(const TransactionInput& in) { if (in.type() == typeid(KeyInput)) { return boost::get(in).outputIndexes.size(); } if (in.type() == typeid(MultisignatureInput)) { return boost::get(in).signatureCount; } return 0; } uint64_t getTransactionInputAmount(const TransactionInput& in) { if (in.type() == typeid(KeyInput)) { return boost::get(in).amount; } if (in.type() == typeid(MultisignatureInput)) { return boost::get(in).amount; } return 0; } TransactionTypes::InputType getTransactionInputType(const TransactionInput& in) { if (in.type() == typeid(KeyInput)) { return TransactionTypes::InputType::Key; } if (in.type() == typeid(MultisignatureInput)) { return TransactionTypes::InputType::Multisignature; } if (in.type() == typeid(BaseInput)) { return TransactionTypes::InputType::Generating; } return TransactionTypes::InputType::Invalid; } const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index) { if (transaction.inputs.size() <= index) { throw std::runtime_error("Transaction input index out of range"); } return transaction.inputs[index]; } const TransactionInput& getInputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::InputType type) { const auto& input = getInputChecked(transaction, index); if (getTransactionInputType(input) != type) { throw std::runtime_error("Unexpected transaction input type"); } return input; } // TransactionOutput helper functions TransactionTypes::OutputType getTransactionOutputType(const TransactionOutputTarget& out) { if (out.type() == typeid(KeyOutput)) { return TransactionTypes::OutputType::Key; } if (out.type() == typeid(MultisignatureOutput)) { return TransactionTypes::OutputType::Multisignature; } return TransactionTypes::OutputType::Invalid; } const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index) { if (transaction.outputs.size() <= index) { throw std::runtime_error("Transaction output index out of range"); } return transaction.outputs[index]; } const TransactionOutput& getOutputChecked(const CryptoNote::TransactionPrefix& transaction, size_t index, TransactionTypes::OutputType type) { const auto& output = getOutputChecked(transaction, index); if (getTransactionOutputType(output.target) != type) { throw std::runtime_error("Unexpected transaction output target type"); } return output; } bool isOutToKey(const Crypto::PublicKey& spendPublicKey, const Crypto::PublicKey& outKey, const Crypto::KeyDerivation& derivation, size_t keyIndex) { Crypto::PublicKey pk; derive_public_key(derivation, keyIndex, spendPublicKey, pk); return pk == outKey; } bool findOutputsToAccount(const CryptoNote::TransactionPrefix& transaction, const AccountPublicAddress& addr, const SecretKey& viewSecretKey, std::vector& out, uint64_t& amount) { AccountKeys keys; keys.address = addr; // only view secret key is used, spend key is not needed keys.viewSecretKey = viewSecretKey; Crypto::PublicKey txPubKey = getTransactionPublicKeyFromExtra(transaction.extra); amount = 0; size_t keyIndex = 0; uint32_t outputIndex = 0; Crypto::KeyDerivation derivation; generate_key_derivation(txPubKey, keys.viewSecretKey, derivation); for (const TransactionOutput& o : transaction.outputs) { assert(o.target.type() == typeid(KeyOutput) || o.target.type() == typeid(MultisignatureOutput)); if (o.target.type() == typeid(KeyOutput)) { if (is_out_to_acc(keys, boost::get(o.target), derivation, keyIndex)) { out.push_back(outputIndex); amount += o.amount; } ++keyIndex; } else if (o.target.type() == typeid(MultisignatureOutput)) { const auto& target = boost::get(o.target); for (const auto& key : target.keys) { if (isOutToKey(keys.address.spendPublicKey, key, derivation, static_cast(outputIndex))) { out.push_back(outputIndex); } ++keyIndex; } } ++outputIndex; } return true; } }