// 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 "TransactionBuilder.h" #include "CryptoNoteCore/TransactionExtra.h" #include "CryptoNoteCore/CryptoNoteTools.h" using namespace CryptoNote; using namespace Crypto; using namespace Common; TransactionBuilder::TransactionBuilder(const CryptoNote::Currency& currency, uint64_t unlockTime) : m_currency(currency), m_version(CryptoNote::CURRENT_TRANSACTION_VERSION), m_unlockTime(unlockTime), m_txKey(generateKeyPair()) {} TransactionBuilder& TransactionBuilder::newTxKeys() { m_txKey = generateKeyPair(); return *this; } TransactionBuilder& TransactionBuilder::setTxKeys(const CryptoNote::KeyPair& txKeys) { m_txKey = txKeys; return *this; } TransactionBuilder& TransactionBuilder::setInput(const std::vector& sources, const CryptoNote::AccountKeys& senderKeys) { m_sources = sources; m_senderKeys = senderKeys; return *this; } TransactionBuilder& TransactionBuilder::addMultisignatureInput(const MultisignatureSource& source) { m_msigSources.push_back(source); return *this; } TransactionBuilder& TransactionBuilder::setOutput(const std::vector& destinations) { m_destinations = destinations; return *this; } TransactionBuilder& TransactionBuilder::addOutput(const CryptoNote::TransactionDestinationEntry& dest) { m_destinations.push_back(dest); return *this; } TransactionBuilder& TransactionBuilder::addMultisignatureOut(uint64_t amount, const KeysVector& keys, uint32_t required) { MultisignatureDestination dst; dst.amount = amount; dst.keys = keys; dst.requiredSignatures = required; m_msigDestinations.push_back(dst); return *this; } Transaction TransactionBuilder::build() const { Crypto::Hash prefixHash; Transaction tx; addTransactionPublicKeyToExtra(tx.extra, m_txKey.publicKey); tx.version = static_cast(m_version); tx.unlockTime = m_unlockTime; std::vector contexts; fillInputs(tx, contexts); fillOutputs(tx); getObjectHash(*static_cast(&tx), prefixHash); signSources(prefixHash, contexts, tx); return tx; } void TransactionBuilder::fillInputs(Transaction& tx, std::vector& contexts) const { for (const TransactionSourceEntry& src_entr : m_sources) { contexts.push_back(KeyPair()); KeyPair& in_ephemeral = contexts.back(); Crypto::KeyImage img; generate_key_image_helper(m_senderKeys, src_entr.realTransactionPublicKey, src_entr.realOutputIndexInTransaction, in_ephemeral, img); // put key image into tx input KeyInput input_to_key; input_to_key.amount = src_entr.amount; input_to_key.keyImage = img; // fill outputs array and use relative offsets for (const TransactionSourceEntry::OutputEntry& out_entry : src_entr.outputs) { input_to_key.outputIndexes.push_back(out_entry.first); } input_to_key.outputIndexes = absolute_output_offsets_to_relative(input_to_key.outputIndexes); tx.inputs.push_back(input_to_key); } for (const auto& msrc : m_msigSources) { tx.inputs.push_back(msrc.input); } } void TransactionBuilder::fillOutputs(Transaction& tx) const { size_t output_index = 0; for(const auto& dst_entr : m_destinations) { Crypto::KeyDerivation derivation; Crypto::PublicKey out_eph_public_key; Crypto::generate_key_derivation(dst_entr.addr.viewPublicKey, m_txKey.secretKey, derivation); Crypto::derive_public_key(derivation, output_index, dst_entr.addr.spendPublicKey, out_eph_public_key); TransactionOutput out; out.amount = dst_entr.amount; KeyOutput tk; tk.key = out_eph_public_key; out.target = tk; tx.outputs.push_back(out); output_index++; } for (const auto& mdst : m_msigDestinations) { TransactionOutput out; MultisignatureOutput target; target.requiredSignatureCount = mdst.requiredSignatures; for (const auto& key : mdst.keys) { Crypto::KeyDerivation derivation; Crypto::PublicKey ephemeralPublicKey; Crypto::generate_key_derivation(key.address.viewPublicKey, m_txKey.secretKey, derivation); Crypto::derive_public_key(derivation, output_index, key.address.spendPublicKey, ephemeralPublicKey); target.keys.push_back(ephemeralPublicKey); } out.amount = mdst.amount; out.target = target; tx.outputs.push_back(out); output_index++; } } void TransactionBuilder::signSources(const Crypto::Hash& prefixHash, const std::vector& contexts, Transaction& tx) const { tx.signatures.clear(); size_t i = 0; // sign TransactionInputToKey sources for (const auto& src_entr : m_sources) { std::vector keys_ptrs; for (const auto& o : src_entr.outputs) { keys_ptrs.push_back(&o.second); } tx.signatures.push_back(std::vector()); std::vector& sigs = tx.signatures.back(); sigs.resize(src_entr.outputs.size()); generate_ring_signature(prefixHash, boost::get(tx.inputs[i]).keyImage, keys_ptrs, contexts[i].secretKey, src_entr.realOutput, sigs.data()); i++; } // sign multisignature source for (const auto& msrc : m_msigSources) { tx.signatures.resize(tx.signatures.size() + 1); auto& outsigs = tx.signatures.back(); for (const auto& key : msrc.keys) { Crypto::KeyDerivation derivation; Crypto::PublicKey ephemeralPublicKey; Crypto::SecretKey ephemeralSecretKey; Crypto::generate_key_derivation(msrc.srcTxPubKey, key.viewSecretKey, derivation); Crypto::derive_public_key(derivation, msrc.srcOutputIndex, key.address.spendPublicKey, ephemeralPublicKey); Crypto::derive_secret_key(derivation, msrc.srcOutputIndex, key.spendSecretKey, ephemeralSecretKey); Crypto::Signature sig; Crypto::generate_signature(prefixHash, ephemeralPublicKey, ephemeralSecretKey, sig); outsigs.push_back(sig); } } }