// 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 "TransactionExtra.h" #include "Common/MemoryInputStream.h" #include "Common/StreamTools.h" #include "Common/StringTools.h" #include "CryptoNoteTools.h" #include "Serialization/BinaryOutputStreamSerializer.h" #include "Serialization/BinaryInputStreamSerializer.h" using namespace Crypto; using namespace Common; namespace CryptoNote { bool parseTransactionExtra(const std::vector &transactionExtra, std::vector &transactionExtraFields) { transactionExtraFields.clear(); if (transactionExtra.empty()) return true; try { MemoryInputStream iss(transactionExtra.data(), transactionExtra.size()); BinaryInputStreamSerializer ar(iss); int c = 0; while (!iss.endOfStream()) { c = read(iss); switch (c) { case TX_EXTRA_TAG_PADDING: { size_t size = 1; for (; !iss.endOfStream() && size <= TX_EXTRA_PADDING_MAX_COUNT; ++size) { if (read(iss) != 0) { return false; // all bytes should be zero } } if (size > TX_EXTRA_PADDING_MAX_COUNT) { return false; } transactionExtraFields.push_back(TransactionExtraPadding{ size }); break; } case TX_EXTRA_TAG_PUBKEY: { TransactionExtraPublicKey extraPk; ar(extraPk.publicKey, "public_key"); transactionExtraFields.push_back(extraPk); break; } case TX_EXTRA_NONCE: { TransactionExtraNonce extraNonce; uint8_t size = read(iss); if (size > 0) { extraNonce.nonce.resize(size); read(iss, extraNonce.nonce.data(), extraNonce.nonce.size()); } transactionExtraFields.push_back(extraNonce); break; } } } } catch (std::exception &) { return false; } return true; } struct ExtraSerializerVisitor : public boost::static_visitor { std::vector& extra; ExtraSerializerVisitor(std::vector& tx_extra) : extra(tx_extra) {} bool operator()(const TransactionExtraPadding& t) { if (t.size > TX_EXTRA_PADDING_MAX_COUNT) { return false; } extra.insert(extra.end(), t.size, 0); return true; } bool operator()(const TransactionExtraPublicKey& t) { return addTransactionPublicKeyToExtra(extra, t.publicKey); } bool operator()(const TransactionExtraNonce& t) { return addExtraNonceToTransactionExtra(extra, t.nonce); } }; bool writeTransactionExtra(std::vector& tx_extra, const std::vector& tx_extra_fields) { ExtraSerializerVisitor visitor(tx_extra); for (const auto& tag : tx_extra_fields) { if (!boost::apply_visitor(visitor, tag)) { return false; } } return true; } PublicKey getTransactionPublicKeyFromExtra(const std::vector& tx_extra) { std::vector tx_extra_fields; parseTransactionExtra(tx_extra, tx_extra_fields); TransactionExtraPublicKey pub_key_field; if (!findTransactionExtraFieldByType(tx_extra_fields, pub_key_field)) return boost::value_initialized(); return pub_key_field.publicKey; } bool addTransactionPublicKeyToExtra(std::vector& tx_extra, const PublicKey& tx_pub_key) { tx_extra.resize(tx_extra.size() + 1 + sizeof(PublicKey)); tx_extra[tx_extra.size() - 1 - sizeof(PublicKey)] = TX_EXTRA_TAG_PUBKEY; *reinterpret_cast(&tx_extra[tx_extra.size() - sizeof(PublicKey)]) = tx_pub_key; return true; } bool addExtraNonceToTransactionExtra(std::vector& tx_extra, const BinaryArray& extra_nonce) { if (extra_nonce.size() > TX_EXTRA_NONCE_MAX_COUNT) { return false; } size_t start_pos = tx_extra.size(); tx_extra.resize(tx_extra.size() + 2 + extra_nonce.size()); //write tag tx_extra[start_pos] = TX_EXTRA_NONCE; //write len ++start_pos; tx_extra[start_pos] = static_cast(extra_nonce.size()); //write data ++start_pos; memcpy(&tx_extra[start_pos], extra_nonce.data(), extra_nonce.size()); return true; } void setPaymentIdToTransactionExtraNonce(std::vector& extra_nonce, const Hash& payment_id) { extra_nonce.clear(); extra_nonce.push_back(TX_EXTRA_NONCE_PAYMENT_ID); const uint8_t* payment_id_ptr = reinterpret_cast(&payment_id); std::copy(payment_id_ptr, payment_id_ptr + sizeof(payment_id), std::back_inserter(extra_nonce)); } bool getPaymentIdFromTransactionExtraNonce(const std::vector& extra_nonce, Hash& payment_id) { if (sizeof(Hash) + 1 != extra_nonce.size()) return false; if (TX_EXTRA_NONCE_PAYMENT_ID != extra_nonce[0]) return false; payment_id = *reinterpret_cast(extra_nonce.data() + 1); return true; } bool parsePaymentId(const std::string& paymentIdString, Hash& paymentId) { return Common::podFromHex(paymentIdString, paymentId); } bool createTxExtraWithPaymentId(const std::string& paymentIdString, std::vector& extra) { Hash paymentIdBin; if (!parsePaymentId(paymentIdString, paymentIdBin)) { return false; } std::vector extraNonce; CryptoNote::setPaymentIdToTransactionExtraNonce(extraNonce, paymentIdBin); if (!CryptoNote::addExtraNonceToTransactionExtra(extra, extraNonce)) { return false; } return true; } bool getPaymentIdFromTxExtra(const std::vector& extra, Hash& paymentId) { std::vector tx_extra_fields; if (!parseTransactionExtra(extra, tx_extra_fields)) { return false; } TransactionExtraNonce extra_nonce; if (findTransactionExtraFieldByType(tx_extra_fields, extra_nonce)) { if (!getPaymentIdFromTransactionExtraNonce(extra_nonce.nonce, paymentId)) { return false; } } else { return false; } return true; } }