186 lines
6 KiB
C++
186 lines
6 KiB
C++
// Copyright (c) 2011-2015 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<CryptoNote::TransactionSourceEntry>& 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<CryptoNote::TransactionDestinationEntry>& 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<uint8_t>(m_version);
|
|
tx.unlockTime = m_unlockTime;
|
|
|
|
std::vector<CryptoNote::KeyPair> contexts;
|
|
|
|
fillInputs(tx, contexts);
|
|
fillOutputs(tx);
|
|
|
|
getObjectHash(*static_cast<TransactionPrefix*>(&tx), prefixHash);
|
|
|
|
signSources(prefixHash, contexts, tx);
|
|
|
|
return tx;
|
|
}
|
|
|
|
void TransactionBuilder::fillInputs(Transaction& tx, std::vector<CryptoNote::KeyPair>& 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<CryptoNote::KeyPair>& contexts, Transaction& tx) const {
|
|
|
|
tx.signatures.clear();
|
|
|
|
size_t i = 0;
|
|
|
|
// sign TransactionInputToKey sources
|
|
for (const auto& src_entr : m_sources) {
|
|
std::vector<const Crypto::PublicKey*> keys_ptrs;
|
|
|
|
for (const auto& o : src_entr.outputs) {
|
|
keys_ptrs.push_back(&o.second);
|
|
}
|
|
|
|
tx.signatures.push_back(std::vector<Crypto::Signature>());
|
|
std::vector<Crypto::Signature>& sigs = tx.signatures.back();
|
|
sigs.resize(src_entr.outputs.size());
|
|
generate_ring_signature(prefixHash, boost::get<KeyInput>(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);
|
|
}
|
|
}
|
|
}
|