Skip to content

Commit

Permalink
Applied TRTL turtlecoin#931
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoStehlik committed Nov 24, 2019
1 parent 64d6a62 commit 3a69444
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 51 deletions.
19 changes: 19 additions & 0 deletions src/common/Varint.h
Expand Up @@ -10,6 +10,7 @@
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

namespace Tools
{
Expand Down Expand Up @@ -69,4 +70,22 @@ namespace Tools
{
return read_varint<std::numeric_limits<T>::digits, InputIt, T>(std::move(first), std::move(last), i);
}

inline std::vector<uint8_t> uintToVarintVector(size_t i)
{
std::vector<uint8_t> output;

while (i >= 0x80)
{
char elem = (static_cast<char>(i) & 0x7f) | 0x80;

output.push_back(elem);

i >>= 7;
}

output.push_back(static_cast<char>(i));

return output;
}
} // namespace Tools
3 changes: 3 additions & 0 deletions src/config/Constants.h
Expand Up @@ -197,6 +197,9 @@ namespace Constants
/* Indicates the following data is a merge mine depth+merkle root */
const uint8_t TX_EXTRA_MERGE_MINING_IDENTIFIER = 0x03;

/* Indicates the following data is arbitrary data in tx_extra */
const uint8_t TX_EXTRA_ARBITRARY_DATA_IDENTIFIER = 0x7f;

const Crypto::Hash NULL_HASH = Crypto::Hash({
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
});
Expand Down
4 changes: 4 additions & 0 deletions src/errors/Errors.cpp
Expand Up @@ -277,6 +277,10 @@ std::string Error::getErrorMessage() const
case INVALID_PRIVATE_KEY:
{
return "The private key given is not a valid ed25519 public key.";
}
case INVALID_EXTRA_DATA:
{
return "The extra data given for the transaction could not be decoded.";
}
/* No default case so the compiler warns us if we missed one */
}
Expand Down
3 changes: 3 additions & 0 deletions src/errors/Errors.h
Expand Up @@ -210,6 +210,9 @@ enum ErrorCode

/* Not on ed25519 curve */
INVALID_PRIVATE_KEY = 52,

/* Extra data for transaction is not a valid hexadecimal string */
INVALID_EXTRA_DATA = 53,
};

class Error
Expand Down
124 changes: 97 additions & 27 deletions src/utilities/ParseExtra.cpp
Expand Up @@ -6,6 +6,7 @@
#include <utilities/ParseExtra.h>
/////////////////////////////////

#include <common/Varint.h>
#include <config/Constants.h>

namespace Utilities
Expand All @@ -28,18 +29,26 @@ namespace Utilities
return parsed.mergedMiningTag;
}

std::vector<uint8_t> getExtraDataFromExtra(const std::vector<uint8_t> &extra)
{
const ParsedExtra parsed = parseExtra(extra);
return parsed.extraData;
}

ParsedExtra parseExtra(const std::vector<uint8_t> &extra)
{
ParsedExtra parsed {Constants::NULL_PUBLIC_KEY, std::string(), {0, Constants::NULL_HASH}};

bool seenPubKey = false;
bool seenNonce = false;
bool seenExtraData = false;
bool seenPaymentID = false;
bool seenMergedMiningTag = false;

for (auto it = extra.begin(); it != extra.end(); it++)
{
/* Nothing else to parse. */
if (seenPubKey && seenPaymentID && seenMergedMiningTag)
if (seenPubKey && seenPaymentID && seenMergedMiningTag && seenExtraData)
{
break;
}
Expand Down Expand Up @@ -69,36 +78,93 @@ namespace Utilities
continue;
}

/* Found a payment ID, and have space for extra nonce size, pid identifier,
and pid. */
/* Found nonce information and need to decode it.
/* Nonce is a sub-tagged field and thus we need to work through
the data to determine what fields are in here */
if (c == Constants::TX_EXTRA_NONCE_IDENTIFIER && elementsRemaining > 1 && !seenNonce)
{
/* Get the length of the following data in the field */
size_t nonceSize = 0;

/* Payment ID looks like this (payment ID is stored in extra nonce)
const auto readNonceSize = Tools::read_varint(it + 1, extra.end(), nonceSize);

[...data...] 0x02 [size of extra nonce] 0x00 [payment ID] [...data...]
/* Set up a variable to hold how much we have read so we know how far to skip ahead */
size_t advanceIterator = readNonceSize;

*/
if (c == Constants::TX_EXTRA_NONCE_IDENTIFIER && elementsRemaining > 1 + 1 + 32
&& *(it + 2) == Constants::TX_EXTRA_PAYMENT_ID_IDENTIFIER && !seenPaymentID)
{
const auto dataBegin = it + 3;
/* Only start reading if there are enough bytes left to read */
if (elementsRemaining > readNonceSize + nonceSize)
{
/* Copy the nonce data into a new array to make things easier to read through */
std::vector<uint8_t> nonceData;

std::copy(it + 1 + readNonceSize, it + 1 + readNonceSize + nonceSize, std::back_inserter(nonceData));

/* Loop through the nonce data looking for fields */
for (auto is = nonceData.begin(); is != nonceData.end(); is++)
{
const uint8_t s = *is;

const auto nElementsRemaining = std::distance(is, nonceData.end());

/* If we encounter a Payment ID field and there are enough bytes remaining in
the nonce data and we have not encountered a payment ID, then read it out */
if (s == Constants::TX_EXTRA_PAYMENT_ID_IDENTIFIER && nElementsRemaining > 32 && !seenPaymentID)
{
/* Grab the payment ID hash out of the nonce data */
Crypto::Hash paymentIDHash;

Crypto::Hash paymentIDHash;
std::copy(is + 1, is + 1 + 32, std::begin(paymentIDHash.data));

/* Copy the payment ID into the hash */
std::copy(dataBegin, dataBegin + 32, std::begin(paymentIDHash.data));
/* Assign the payment ID to the parsed result */
parsed.paymentID = Common::podToHex(paymentIDHash);

/* Convert to a string */
std::string paymentID = Common::podToHex(paymentIDHash);
seenPaymentID = true;

/* Convert it to lower case */
std::transform(paymentID.begin(), paymentID.end(), paymentID.begin(), ::tolower);
/* Advance the main iterator by the tag (1-byte) + the hash size (32-bytes) */
advanceIterator += 1 + 32;

parsed.paymentID = paymentID;
/* Advance the inner iterator by the hash size (32-bytes) */
is += 32;

/* Advance past the payment ID */
it += 32 + 1 + 1;
continue;
}

seenPaymentID = true;
/* If we encounter an arbitrary data field and there is at least one byte remaining
and we have not encountered the arbitrary data yet, then read it out */
if (s == Constants::TX_EXTRA_ARBITRARY_DATA_IDENTIFIER && nElementsRemaining > 1 && !seenExtraData)
{
/* Read out the size of the data */
size_t dataSize = 0;

const auto readDataSize = Tools::read_varint(is + 1, nonceData.end(), dataSize);

/* If there are enough bytes left to read based upon the size above then
read out the data */
if (nElementsRemaining >= 1 + readDataSize + dataSize)
{
/* Copy the data into the parsed extraData field */
std::copy(is + 1 + readDataSize, is + 1 + readDataSize + dataSize, std::back_inserter(parsed.extraData));

seenExtraData = true;

/* Advance the main iterator by the tag (1-byte) + the length field (readDataSize)
+ the actual data size (dataSize) */
advanceIterator += 1 + readDataSize + dataSize;

/* Advance the inner iterator by the the length field (readDataSize)
+ the actual data size (dataSize) */
is += readDataSize + dataSize;

continue;
}
}
}
}

/* Advance the main iterator by the amount found in the nonce data */
it += advanceIterator;

seenNonce = true;

/* And continue parsing */
continue;
Expand All @@ -107,23 +173,27 @@ namespace Utilities
if (c == Constants::TX_EXTRA_MERGE_MINING_IDENTIFIER && elementsRemaining > 1 && !seenMergedMiningTag)
{
/* Get the length of the following data (Probably 33 bytes for depth+hash) */
const uint8_t dataSize = *(it + 1);
size_t dataSize = 0;

if (elementsRemaining > dataSize + 1 && dataSize >= 33)
const auto readDataSize = Tools::read_varint(it + 1, extra.end(), dataSize);

if (elementsRemaining > dataSize + readDataSize && dataSize >= 33)
{
const uint8_t depth = *(it + 2);
size_t depth = 0;

const auto readDepthSize = Tools::read_varint(it + 1 + readDataSize, extra.end(), depth);

Crypto::Hash merkleRoot;

const auto dataBegin = it + 3;
const auto dataBegin = it + 1 + readDataSize + readDepthSize;

std::copy(dataBegin, dataBegin + 32, std::begin(merkleRoot.data));

parsed.mergedMiningTag.depth = depth;
parsed.mergedMiningTag.merkleRoot = merkleRoot;

/* Advance past the mm tag */
it += dataSize + 1;
/* Advance past the mm tag by length field (readDataSize) + */
it += readDepthSize + 32;

seenMergedMiningTag = true;

Expand Down
3 changes: 3 additions & 0 deletions src/utilities/ParseExtra.h
Expand Up @@ -21,6 +21,7 @@ namespace Utilities
Crypto::PublicKey transactionPublicKey;
std::string paymentID;
MergedMiningTag mergedMiningTag;
std::vector<uint8_t> extraData;
};

std::string getPaymentIDFromExtra(const std::vector<uint8_t> &extra);
Expand All @@ -29,5 +30,7 @@ namespace Utilities

MergedMiningTag getMergedMiningTagFromExtra(const std::vector<uint8_t> &extra);

std::vector<uint8_t> getExtraDataFromExtra(const std::vector<uint8_t> &extra);

ParsedExtra parseExtra(const std::vector<uint8_t> &extra);
} // namespace Utilities
29 changes: 27 additions & 2 deletions src/walletapi/ApiDispatcher.cpp
Expand Up @@ -9,6 +9,7 @@
#include "json.hpp"

#include <config/CryptoNoteConfig.h>
#include <common/StringTools.h>
#include <crypto/random.h>
#include <cryptonotecore/Mixins.h>
#include <cryptopp/modes.h>
Expand Down Expand Up @@ -690,8 +691,20 @@ std::tuple<Error, uint16_t>
unlockTime = getJsonValue<uint64_t>(body, "unlockTime");
}

std::vector<uint8_t> extraData;

if (body.find("extra") != body.end())
{
std::string extra = getJsonValue<std::string>(body, "extra");

if (!Common::fromHex(extra, extraData))
{
return {INVALID_EXTRA_DATA, 400};
}
}

auto [error, hash] = m_walletBackend->sendTransactionAdvanced(
destinations, mixin, fee, paymentID, subWalletsToTakeFrom, changeAddress, unlockTime);
destinations, mixin, fee, paymentID, subWalletsToTakeFrom, changeAddress, unlockTime, extraData);

if (error)
{
Expand Down Expand Up @@ -747,7 +760,19 @@ std::tuple<Error, uint16_t>
subWalletsToTakeFrom = getJsonValue<std::vector<std::string>>(body, "sourceAddresses");
}

auto [error, hash] = m_walletBackend->sendFusionTransactionAdvanced(mixin, subWalletsToTakeFrom, destination);
std::vector<uint8_t> extraData;

if (body.find("extra") != body.end())
{
std::string extra = getJsonValue<std::string>(body, "extra");

if (!Common::fromHex(extra, extraData))
{
return {INVALID_EXTRA_DATA, 400};
}
}

auto [error, hash] = m_walletBackend->sendFusionTransactionAdvanced(mixin, subWalletsToTakeFrom, destination, extraData);

if (error)
{
Expand Down

0 comments on commit 3a69444

Please sign in to comment.