ArkEcosystemArchive / cpp-crypto

A simple Cryptography Implementation in C++ for the Ark Blockchain.
MIT License
12 stars 10 forks source link

bug: Transaction Json Serialization Failure with Ubuntu/Arduino IDE #99

Closed sleepdefic1t closed 5 years ago

sleepdefic1t commented 5 years ago

Expected Behavior

Transaction Json objects serialize correctly on all platforms.

Current Behavior

Transaction Json objects serialize correctly on all platforms except Ubuntu 18.0.4 using the Arduino IDE as reported by Slack user Asmita.

Specifically, numbers are being serialized as string objects.

Possible Solution

A potential solution is to enforce type serialization by declaring int/uint64_t variables. Use of stoull and stoi may also be required per user Asmita.

Steps to Reproduce (for bugs)

  1. Use the Arduino IDE on Ubuntu 18.0.4.
  2. Use the C++ SDK to create a transaction object.
  3. Use the .toJson() method to view the output.

Numbers will be serialized as strings.

Context

I’m currently on vacation, but will investigate a fix as soon as I’m back.

Your Environment

sleepdefic1t commented 5 years ago

Fix coming soon. Just needs some more testing.

Here is an example of the potential solution:

cpp-crypto/src/transactions/transaction.cpp


#include <cstdlib>

#define __STDC_FORMAT_MACROS 1
#include <cinttypes>

...

std::map<std::string, std::string> Ark::Crypto::Transactions::Transaction::toArray() {
  //  buffers for variable and non-string type-values.
  char amount[24];
  char assetName[16];
  char assetValue[512];
  char fee[24];
  char network[8];
  char signatures[512];
  char timestamp[36];
  char type[8];
  char version[8];

  //  Amount
  snprintf(amount, sizeof(amount), "%" PRIu64, this->amount);

  //  Asset
  if (this->type == 0) {
    // Transfer
    // do nothing
  } else if (this->type == 1) {
    //  Second Signature Registration
    strncpy(assetName, "publicKey", sizeof(assetName));
    strncpy(
        assetValue,
        this->asset.signature.publicKey.c_str(),
        this->asset.signature.publicKey.length() + 1);
  } else if (this->type == 2) {
    // Delegate Registration
    strncpy(assetName, "username", sizeof(assetName));
    strncpy(
        assetValue,
        this->asset.delegate.username.c_str(),
        this->asset.delegate.username.length() + 1);
  } else if (this->type == 3) {
    // Vote
    strncpy(assetName, "votes", sizeof(assetName));
    strncpy(assetValue, "", 1);
    for (unsigned int i = 0; i < this->asset.votes.size(); ++i) {
      strncat(
          assetValue,
          this->asset.votes[i].c_str(),
          this->asset.votes[i].length() + 1);
      if (i < this->asset.votes.size() - 1) {
        strncat(assetValue, ",", 1);
      };
    };

  // } else if (this->type == 4) {  //  Multisignature Registration
  //   //  TODO
  // } else if (this->type == 5) {  //  IPFS
  //   //  TBD
  // } else if (this->type == 6) {  //  Timelock Registration
  //   //  TBD
  // } else if (this->type == 7) {  //  Multi-Payment
  //   //  TBD
  // } else if (this->type == 8) {  //  Delegate Resignation
  //   //  TBD
  };

  //  Fee
  snprintf(fee, sizeof(fee), "%" PRIu64,  this->fee);

  //  Signatures
  strcpy(signatures, "");
  for (unsigned int i = 0; i < this->signatures.size(); ++i) {
    strncat(
        signatures,
        this->signatures[i].c_str(),
        this->signatures[i].length() + 1);
    if (i < this->signatures.size() - 1) {
      strncpy(signatures, ",", 1);
    };
  };

  //  Network
  snprintf(network, sizeof(network), "%d", this->network);

  //  Timestamp
  snprintf(timestamp, sizeof(timestamp), "%d", this->timestamp);

  //  Type
  snprintf(type, sizeof(type), "%u", this->type);

  //  Version
  snprintf(version, sizeof(version), "%d", this->version);

  return {
    { "amount", amount },
    { assetName, assetValue },
    { "fee", fee },
    { "id", this->id },
    { "network", network },
    { "recipientId", this->recipientId },
    { "secondSignature", this->secondSignature },
    { "senderPublicKey", this->senderPublicKey },
    { "signature", this->signature },
    { "signatures", signatures },
    { "signSignature", this->signSignature },
    { "timestamp", timestamp },
    { "type", type },
    { "vendorField", this->vendorField },
    { "version", version }
  };
}

/**/

std::string Ark::Crypto::Transactions::Transaction::toJson() {
  std::map<std::string, std::string> txArray = this->toArray();

  const size_t docCapacity = 900;
  DynamicJsonDocument doc(docCapacity);

  //  Amount
  uint64_t amount = strtoull(txArray["amount"].c_str(), nullptr, 10);
  doc["amount"] = amount;

  //  Asset
  if (this->type == 0) {
    // Transfer
    //do nothing
  } else if (this->type == 1) {
    // Second Signature Registration
    JsonObject tAsset = doc.createNestedObject("asset");
    JsonObject signature = tAsset.createNestedObject("signature");
    signature["publicKey"] = txArray["publicKey"];
  } else if (this->type == 2) {
    // Delegate Registration
    JsonObject dAsset = doc.createNestedObject("asset");
    JsonObject delegate = dAsset.createNestedObject("delegate");
    delegate["username"] = txArray["username"];
    delegate["publicKey"] = txArray["publicKey"];
  } else if (this->type == 3) {
    //  Vote
    JsonObject vAsset = doc.createNestedObject("asset");
    JsonArray votes = vAsset.createNestedArray("votes");

    std::string::size_type lastPos = txArray["votes"].find_first_not_of(',', 0);
    std::string::size_type pos = txArray["votes"].find_first_of(',', lastPos);
    while (std::string::npos != pos || std::string::npos != lastPos) {
      votes.add(txArray["votes"].substr(lastPos, pos - lastPos));
      lastPos = txArray["votes"].find_first_not_of(',', pos);
      pos = txArray["votes"].find_first_of(',', lastPos);
    };

  // } else if (this->type == 4) {  //  Multisignature Registration
  //   //  TODO
  // } else if (this->type == 5) {  //  IPFS
  //   //  TBD
  // } else if (this->type == 6) {  //  Timelock Registration
  //   //  TBD
  // } else if (this->type == 7) {  //  Multi-Payment
  //   //  TBD
  // } else if (this->type == 8) {  //  Delegate Resignation
  //   //  TBD
  };

  //  Fee
  uint64_t fee = strtoull(txArray["fee"].c_str(), nullptr, 10);
  doc["fee"] = fee;

  //  Id
  doc["id"] = txArray["id"];

  //  Network
  if (txArray["network"] != "0") {
    const uint8_t* network = reinterpret_cast<const uint8_t*>(
        txArray["network"].c_str());
    doc["network"] = network;
  };

  //  RecipientId
  doc["recipientId"] = txArray["recipientId"];

  //  SecondSignature
  if (std::strlen(txArray["secondSignature"].c_str()) > 0) {
    doc["secondSignature"] = txArray["secondSignature"];
  };

  //  SenderPublicKey
  doc["senderPublicKey"] = txArray["senderPublicKey"];

  //  Signature
  doc["signature"] = txArray["signature"];

  //  Signatures
  if (!this->signatures.empty()) {
    JsonArray signatures = doc.createNestedArray("signatures");
    std::string::size_type lastPos = txArray["signatures"].find_first_not_of(',', 0);
    std::string::size_type pos = txArray["signatures"].find_first_of(',', lastPos);
    while (std::string::npos != pos || std::string::npos != lastPos) {
      signatures.add(txArray["signatures"].substr(lastPos, pos - lastPos));
      lastPos = txArray["signatures"].find_first_not_of(',', pos);
      pos = txArray["signatures"].find_first_of(',', lastPos);
    };
  };

  //  SignSignature
  if (std::strlen(txArray["signSignature"].c_str()) > 0) {
    doc["signSignature"] = txArray["signSignature"];
  };

  //  Timestamp
  uint32_t timestamp = strtoul(txArray["timestamp"].c_str(), nullptr, 10);
  doc["timestamp"] = timestamp;

  //  Type
  const uint8_t* type = reinterpret_cast<const uint8_t*>(
      txArray["type"].c_str());
  doc["type"] = type;

  //  VendorField
  if (std::strlen(txArray["vendorField"].c_str()) > 0) {
    doc["vendorField"] = txArray["vendorField"];
  };

  //  Version
  if (txArray["version"] != "0") {
    const uint8_t* version = reinterpret_cast<const uint8_t*>(
        txArray["version"].c_str());
    doc["version"] = version;
  };

  char jsonChar[docCapacity];
  serializeJson(doc, jsonChar, docCapacity);

  return jsonChar;
}
ciband commented 5 years ago

Sorry to jump in out of the blue.

Are you worried about stack space? That is a goodly amount there.

I think you could eliminate a lot of the locals, it just might make the map construction a bit messy.

Just throwing out ideas, really knowing nothing about the current state of things, lol, so take it for what its worth.

sleepdefic1t commented 5 years ago

The whole toArray() method is kinda bloated regardless, this is just a fix for what’s currently there.

The entire SDK is getting refactored though. Particularly the TX classes, so I haven’t been too focused on optimizing this bit just yet. std::map may not even be the best way to represent this function to begin with. Stricter typing might improve the situation too.

I am definitely open to any and all suggestions though 👍 Thanks for the feedback, brotha ✌️

sleepdefic1t commented 5 years ago

Temp Fix for Arduino v.0.3.0

This is done using the latest ArduinoJson >= v.6

—-

src/utils/message.cpp

/**
 * This file is part of Ark Cpp Crypto.
 *
 * (c) Ark Ecosystem <info@ark.io>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 **/

#include "utils/message.h"
#include "helpers/json.h"

/**
 * @brief Create a message object for checking its validity.
 *
 * @param std::string msg
 * @param PublicKey pubKey
 * @param std::vector<uint8_t> sig
 **/
Ark::Crypto::Utils::Message::Message(
    std::string msg,
    PublicKey pubKey,
    std::vector<uint8_t> sig)
    : message(msg),
      publicKey(pubKey),
      signature(sig){};
/**/

/**
 * @brief Sign a message using the given passphrase.
 *
 * @param string message
 * @param string passphrase
 *
 * @return bool
 **/
bool Ark::Crypto::Utils::Message::sign(std::string newMessage, const char *const passphrase) {
  this->message = newMessage;

  /* Get the PrivateKey */
  PrivateKey privateKey = Ark::Crypto::Identities::PrivateKey::fromPassphrase(passphrase);

  /* Set the PublicKey from the derived PrivateKey */
  this->publicKey = Ark::Crypto::Identities::PublicKey::fromPrivateKey(privateKey);

  /* Get the Hash */
  const auto unsignedMessage = reinterpret_cast<const unsigned char *>(message.c_str());
  const auto hash = Sha256::getHash(unsignedMessage, this->message.length());

  /* Sign it */
  cryptoSign(hash, privateKey, this->signature);

  return this->verify();
};
/**/

/**
 * @brief Verify the message contents.
 *
 * @return bool
 **/
bool Ark::Crypto::Utils::Message::verify() {
  // cast message to unsigned char*
  const auto unsignedMessage = reinterpret_cast<const unsigned char *>(this->message.c_str());
  const auto hash = Sha256::getHash(unsignedMessage, this->message.length());

  return cryptoVerify(this->publicKey, hash, this->signature);
};
/**/

/**
 * @brief Convert the message to its array representation using an array of pairs
 *
 * @return std::map<std::string, std::string>
 **/
std::map<std::string, std::string> Ark::Crypto::Utils::Message::toArray() {
  return {
    {"publickey", this->publicKey.toString()},
    {"signature", BytesToHex(this->signature.begin(), this->signature.end())},
    {"message", this->message}
  };
}
/**/

/**
 * @brief Convert the message to its JSON representation.
 *
 * @return std::string
 **/
std::string Ark::Crypto::Utils::Message::toJson() {
  std::map<std::string, std::string> messageArray = this->toArray();

  const size_t docLength
      = (33 + 1)  // publickey length
      + (72 + 1)  // signature length
      + this->message.length();
  const size_t docCapacity = JSON_OBJECT_SIZE(3) + docLength + 120;

  DynamicJsonDocument doc(docCapacity);

  doc["publickey"] = messageArray["publickey"];
  doc["signature"] = messageArray["signature"];
  doc["message"] = messageArray["message"];

  std::string jsonStr;
  jsonStr.reserve(docCapacity);
  serializeJson(doc, &jsonStr[0], docCapacity);

  return jsonStr;
}
/**/

src/transactions/transaction.cpp

#include "transactions/transaction.h"
#include "bcl/Sha256.hpp"
#include "enums/types.h"
#include "helpers/crypto.h"
#include "helpers/crypto_helpers.h"
#include "helpers/json.h"
#include "identities/address.h"
#include "identities/privatekey.h"

#include <map>

using namespace Ark::Crypto::Identities;

Ark::Crypto::Transactions::Transaction::Transaction() {}

std::string Ark::Crypto::Transactions::Transaction::getId() const {
  auto bytes = this->toBytes(false, false);
  const auto shaHash = Sha256::getHash(&bytes[0], bytes.size());
  memcpy(&bytes[0], shaHash.value, shaHash.HASH_LEN);
  return BytesToHex(&bytes[0], &bytes[0] + shaHash.HASH_LEN);
}

std::string Ark::Crypto::Transactions::Transaction::sign(const char* passphrase) {
  PrivateKey privateKey = PrivateKey::fromPassphrase(passphrase);
  this->senderPublicKey = Identities::PublicKey::fromPrivateKey(privateKey).toString();

  const auto bytes = this->toBytes();
  const auto hash = Sha256::getHash(&bytes[0], bytes.size());

  std::vector<uint8_t> buffer;
  cryptoSign(hash, privateKey, buffer);

  this->signature = BytesToHex(buffer.begin(), buffer.end());
  return this->signature;
}

std::string Ark::Crypto::Transactions::Transaction::secondSign(const char* passphrase) {
  PrivateKey privateKey = PrivateKey::fromPassphrase(passphrase);
  const auto bytes = this->toBytes(false);
  const auto hash = Sha256::getHash(&bytes[0], bytes.size());

  std::vector<uint8_t> buffer;
  cryptoSign(hash, privateKey, buffer);

  this->secondSignature = BytesToHex(buffer.begin(), buffer.end());
  return this->secondSignature;
}

bool Ark::Crypto::Transactions::Transaction::verify() const {
  return this->internalVerify(this->senderPublicKey, this->toBytes(), this->signature);
}

bool Ark::Crypto::Transactions::Transaction::secondVerify(const char* secondPublicKey) const {
  return this->internalVerify(secondPublicKey, this->toBytes(false), this->secondSignature);
}

bool Ark::Crypto::Transactions::Transaction::internalVerify(
    std::string publicKey,
    std::vector<uint8_t> bytes,
    std::string signature) const {
  const auto hash = Sha256::getHash(&bytes[0], bytes.size());
  const auto key = Identities::PublicKey::fromHex(publicKey.c_str());
  auto signatureBytes = HexToBytes(signature.c_str());
  return cryptoVerify(key, hash, signatureBytes);
}

std::vector<uint8_t> Ark::Crypto::Transactions::Transaction::toBytes(
    bool skipSignature,
    bool skipSecondSignature) const {
  std::vector<uint8_t> bytes;

  pack(bytes, this->type);
  pack(bytes, this->timestamp);

  const auto senderKeyBytes = HexToBytes(this->senderPublicKey.c_str());
  bytes.insert(std::end(bytes), std::begin(senderKeyBytes), std::end(senderKeyBytes));

  const auto skipRecipientId = type
    == Enums::Types::SECOND_SIGNATURE_REGISTRATION
    || type == Enums::Types::MULTI_SIGNATURE_REGISTRATION;

  if (!this->recipientId.empty() && !skipRecipientId) {
    std::vector<std::uint8_t> recipientIdBytes = Address::bytesFromBase58Check(this->recipientId.c_str());
    bytes.insert(std::end(bytes), std::begin(recipientIdBytes), std::end(recipientIdBytes));
  } else {
    std::vector<uint8_t> filler(21, 0);
    bytes.insert(std::end(bytes), std::begin(filler), std::end(filler));
  }

  if (!this->vendorField.empty()) {
    bytes.insert(std::end(bytes), std::begin(this->vendorField), std::end(this->vendorField));

    size_t diff = 64 - vendorField.length();
    if (diff > 0) {
      std::vector<uint8_t> filler(diff, 0);
      bytes.insert(std::end(bytes), std::begin(filler), std::end(filler));
    }

  } else {
    std::vector<uint8_t> filler(64, 0);
    bytes.insert(std::end(bytes), std::begin(filler), std::end(filler));
  }

  pack(bytes, this->amount);
  pack(bytes, this->fee);

  if (type == Enums::Types::SECOND_SIGNATURE_REGISTRATION) {
    const auto publicKeyBytes = HexToBytes(this->asset.signature.publicKey.c_str());
    bytes.insert(std::end(bytes), std::begin(publicKeyBytes), std::end(publicKeyBytes));

  } else if (type == Enums::Types::DELEGATE_REGISTRATION) {
    bytes.insert(std::end(bytes), std::begin(this->asset.delegate.username), std::end(this->asset.delegate.username));

  } else if (type == Enums::Types::VOTE) {
    const auto joined = join(this->asset.votes);
    bytes.insert(std::end(bytes), std::begin(joined), std::end(joined));

  } else if (type == Enums::Types::MULTI_SIGNATURE_REGISTRATION) {
    pack(bytes, this->asset.multiSignature.min);
    pack(bytes, this->asset.multiSignature.lifetime);
    const auto joined = join(this->asset.multiSignature.keysgroup);
    bytes.insert(std::end(bytes), std::begin(joined), std::end(joined));
  }

  if (!skipSignature && !this->signature.empty()) {
    const auto signatureBytes = HexToBytes(this->signature.c_str());
    bytes.insert(std::end(bytes), std::begin(signatureBytes), std::end(signatureBytes));
  }

  if (!skipSecondSignature && !this->secondSignature.empty()) {
    const auto secondSignatureBytes = HexToBytes(this->secondSignature.c_str());
    bytes.insert(std::end(bytes), std::begin(secondSignatureBytes), std::end(secondSignatureBytes));
  }

  return bytes;
}

std::map<std::string, std::string> Ark::Crypto::Transactions::Transaction::toArray() {
  //  buffers for variable and non-string type-values.
  char amount[24], assetName[16], assetValue[512], fee[24], network[8], signatures[512], timestamp[36], type[8], version[8];

  //  Amount
  sprintf(amount, "%llu", this->amount);

  //  Asset
  if (this->type == 0) {  //  Transfer
    //do nothing
  } else if (this->type == 1) { //  Second Signature Registration

    strcpy(assetName, "publicKey");
    strcpy(assetValue, this->asset.signature.publicKey.c_str());

  } else if (this->type == 2) { //  Delegate Registration

    strcpy(assetName, "username");
    strcpy(assetValue, this->asset.delegate.username.c_str());

  } else if (this->type == 3) {  //  Vote

    strcpy(assetName, "votes");
    strcpy(assetValue, "");
    for (unsigned int i = 0; i < this->asset.votes.size(); ++i) {
      strcat(assetValue, this->asset.votes[i].c_str());

      if (i < this->asset.votes.size() - 1) {
        strcat(assetValue, ",");
      }
    }

  // } else if (this->type == 4) {  //  Multisignature Registration
  //   //  TODO
  // } else if (this->type == 5) {  //  IPFS
  //   //  TBD
  // } else if (this->type == 6) {  //  Timelock Registration
  //   //  TBD
  // } else if (this->type == 7) {  //  Multi-Payment
  //   //  TBD
  // } else if (this->type == 8) {  //  Delegate Resignation
  //   //  TBD
  };

  //  Fee
  // fee << this->fee;
  sprintf(fee, "%llu",  this->fee);

  //  Signatures
  strcpy(signatures, "");
  for (unsigned int i = 0; i < this->signatures.size(); ++i) {
    strcat(signatures, this->signatures[i].c_str());
    if (i < this->signatures.size() - 1) {
      strcpy(signatures, ",");
    }
  }

  //  Network
  sprintf(network, "%d", this->network);

  //  Timestamp
  sprintf(timestamp, "%d", this->timestamp);

  //  Type
  sprintf(type, "%d", this->type);

  //  Version
  sprintf(version, "%d", this->version);

  return {
    {"amount", amount},
    {assetName, assetValue},
    {"fee", fee},
    {"id", this->id},
    {"network", network},
    {"recipientId", this->recipientId},
    {"secondSignature", this->secondSignature},
    {"senderPublicKey", this->senderPublicKey},
    {"signature", this->signature},
    {"signatures", signatures},
    {"signSignature", this->signSignature},
    {"timestamp", timestamp},
    {"type", type},
    {"vendorField", this->vendorField},
    {"version", version}
  };
}

std::string Ark::Crypto::Transactions::Transaction::toJson() {
  std::map<std::string, std::string> txArray = this->toArray();

  const size_t docCapacity = 900;
  DynamicJsonDocument doc(docCapacity);

  //  Amount
  doc["amount"] = strtoull(txArray["amount"].c_str(), nullptr, 10);

  //  Asset
  if (this->type == 0) {
    // Transfer
    //do nothing
  } else if (this->type == 1) {
    // Second Signature Registration
    JsonObject tAsset = doc.createNestedObject("asset");
    JsonObject signature = tAsset.createNestedObject("signature");
    signature["publicKey"] = txArray["publicKey"];
  } else if (this->type == 2) {
    // Delegate Registration
    JsonObject dAsset = doc.createNestedObject("asset");
    JsonObject delegate = dAsset.createNestedObject("delegate");
    delegate["username"] = txArray["username"];
    delegate["publicKey"] = txArray["publicKey"];
  } else if (this->type == 3) {
    //  Vote
    JsonObject vAsset = doc.createNestedObject("asset");
    JsonArray votes = vAsset.createNestedArray("votes");

    std::string::size_type lastPos = txArray["votes"].find_first_not_of(',', 0);
    std::string::size_type pos = txArray["votes"].find_first_of(',', lastPos);
    while (std::string::npos != pos || std::string::npos != lastPos) {
      votes.add(txArray["votes"].substr(lastPos, pos - lastPos));
      lastPos = txArray["votes"].find_first_not_of(',', pos);
      pos = txArray["votes"].find_first_of(',', lastPos);
    };

  // } else if (this->type == 4) {  //  Multisignature Registration
  //   //  TODO
  // } else if (this->type == 5) {  //  IPFS
  //   //  TBD
  // } else if (this->type == 6) {  //  Timelock Registration
  //   //  TBD
  // } else if (this->type == 7) {  //  Multi-Payment
  //   //  TBD
  // } else if (this->type == 8) {  //  Delegate Resignation
  //   //  TBD
  };

  //  Fee
  doc["fee"] = strtoull(txArray["fee"].c_str(), nullptr, 10);

  //  Id
  doc["id"] = txArray["id"];

  //  Network
  if (txArray["network"] != "0") {
    doc["network"] = atoi(txArray["network"].c_str());
  };

  //  RecipientId
  doc["recipientId"] = txArray["recipientId"];

  //  SecondSignature
  if (std::strlen(txArray["secondSignature"].c_str()) > 0) {
    doc["secondSignature"] = txArray["secondSignature"];
  };

  //  SenderPublicKey
  doc["senderPublicKey"] = txArray["senderPublicKey"];

  //  Signature
  doc["signature"] = txArray["signature"];

  //  Signatures
  if (!this->signatures.empty()) {
    JsonArray signatures = doc.createNestedArray("signatures");
    std::string::size_type lastPos = txArray["signatures"].find_first_not_of(',', 0);
    std::string::size_type pos = txArray["signatures"].find_first_of(',', lastPos);
    while (std::string::npos != pos || std::string::npos != lastPos) {
      signatures.add(txArray["signatures"].substr(lastPos, pos - lastPos));
      lastPos = txArray["signatures"].find_first_not_of(',', pos);
      pos = txArray["signatures"].find_first_of(',', lastPos);
    };
  };

  //  SignSignature
  if (std::strlen(txArray["signSignature"].c_str()) > 0) {
    doc["signSignature"] = txArray["signSignature"];
  };

  //  Timestamp
  doc["timestamp"] = strtoul(txArray["timestamp"].c_str(), nullptr, 10);

  //  Type
  doc["type"] = atoi(txArray["type"].c_str());

  //  VendorField
  if (std::strlen(txArray["vendorField"].c_str()) > 0) {
    doc["vendorField"] = txArray["vendorField"];
  };

  //  Version
  if (txArray["version"] != "0") {
    doc["version"] = atoi(txArray["version"].c_str());
  };

  char jsonChar[docCapacity];
  serializeJson(doc, jsonChar, docCapacity);

  return jsonChar;
}
sleepdefic1t commented 5 years ago

Temp Fix for Arduino v.0.3.0 w/ArduinoJson ~v.5.13

src/transactions/transaction.cpp

#include "transactions/transaction.h"
#include "bcl/Sha256.hpp"
#include "enums/types.h"
#include "helpers/crypto.h"
#include "helpers/crypto_helpers.h"
#include "helpers/json.h"
#include "identities/address.h"
#include "identities/privatekey.h"

#include <map>

using namespace Ark::Crypto::Identities;

Ark::Crypto::Transactions::Transaction::Transaction() {}

std::string Ark::Crypto::Transactions::Transaction::getId() const {
  auto bytes = this->toBytes(false, false);
  const auto shaHash = Sha256::getHash(&bytes[0], bytes.size());
  memcpy(&bytes[0], shaHash.value, shaHash.HASH_LEN);
  return BytesToHex(&bytes[0], &bytes[0] + shaHash.HASH_LEN);
}

std::string Ark::Crypto::Transactions::Transaction::sign(const char* passphrase) {
  PrivateKey privateKey = PrivateKey::fromPassphrase(passphrase);
  this->senderPublicKey = Identities::PublicKey::fromPrivateKey(privateKey).toString();

  const auto bytes = this->toBytes();
  const auto hash = Sha256::getHash(&bytes[0], bytes.size());

  std::vector<uint8_t> buffer;
  cryptoSign(hash, privateKey, buffer);

  this->signature = BytesToHex(buffer.begin(), buffer.end());
  return this->signature;
}

std::string Ark::Crypto::Transactions::Transaction::secondSign(const char* passphrase) {
  PrivateKey privateKey = PrivateKey::fromPassphrase(passphrase);
  const auto bytes = this->toBytes(false);
  const auto hash = Sha256::getHash(&bytes[0], bytes.size());

  std::vector<uint8_t> buffer;
  cryptoSign(hash, privateKey, buffer);

  this->secondSignature = BytesToHex(buffer.begin(), buffer.end());
  return this->secondSignature;
}

bool Ark::Crypto::Transactions::Transaction::verify() const {
  return this->internalVerify(this->senderPublicKey, this->toBytes(), this->signature);
}

bool Ark::Crypto::Transactions::Transaction::secondVerify(const char* secondPublicKey) const {
  return this->internalVerify(secondPublicKey, this->toBytes(false), this->secondSignature);
}

bool Ark::Crypto::Transactions::Transaction::internalVerify(
    std::string publicKey,
    std::vector<uint8_t> bytes,
    std::string signature) const {
  const auto hash = Sha256::getHash(&bytes[0], bytes.size());
  const auto key = Identities::PublicKey::fromHex(publicKey.c_str());
  auto signatureBytes = HexToBytes(signature.c_str());
  return cryptoVerify(key, hash, signatureBytes);
}

std::vector<uint8_t> Ark::Crypto::Transactions::Transaction::toBytes(
    bool skipSignature,
    bool skipSecondSignature) const {
  std::vector<uint8_t> bytes;

  pack(bytes, this->type);
  pack(bytes, this->timestamp);

  const auto senderKeyBytes = HexToBytes(this->senderPublicKey.c_str());
  bytes.insert(std::end(bytes), std::begin(senderKeyBytes), std::end(senderKeyBytes));

  const auto skipRecipientId = type
    == Enums::Types::SECOND_SIGNATURE_REGISTRATION
    || type == Enums::Types::MULTI_SIGNATURE_REGISTRATION;

  if (!this->recipientId.empty() && !skipRecipientId) {
    std::vector<std::uint8_t> recipientIdBytes = Address::bytesFromBase58Check(this->recipientId.c_str());
    bytes.insert(std::end(bytes), std::begin(recipientIdBytes), std::end(recipientIdBytes));
  } else {
    std::vector<uint8_t> filler(21, 0);
    bytes.insert(std::end(bytes), std::begin(filler), std::end(filler));
  }

  if (!this->vendorField.empty()) {
    bytes.insert(std::end(bytes), std::begin(this->vendorField), std::end(this->vendorField));

    size_t diff = 64 - vendorField.length();
    if (diff > 0) {
      std::vector<uint8_t> filler(diff, 0);
      bytes.insert(std::end(bytes), std::begin(filler), std::end(filler));
    }

  } else {
    std::vector<uint8_t> filler(64, 0);
    bytes.insert(std::end(bytes), std::begin(filler), std::end(filler));
  }

  pack(bytes, this->amount);
  pack(bytes, this->fee);

  if (type == Enums::Types::SECOND_SIGNATURE_REGISTRATION) {
    const auto publicKeyBytes = HexToBytes(this->asset.signature.publicKey.c_str());
    bytes.insert(std::end(bytes), std::begin(publicKeyBytes), std::end(publicKeyBytes));

  } else if (type == Enums::Types::DELEGATE_REGISTRATION) {
    bytes.insert(std::end(bytes), std::begin(this->asset.delegate.username), std::end(this->asset.delegate.username));

  } else if (type == Enums::Types::VOTE) {
    const auto joined = join(this->asset.votes);
    bytes.insert(std::end(bytes), std::begin(joined), std::end(joined));

  } else if (type == Enums::Types::MULTI_SIGNATURE_REGISTRATION) {
    pack(bytes, this->asset.multiSignature.min);
    pack(bytes, this->asset.multiSignature.lifetime);
    const auto joined = join(this->asset.multiSignature.keysgroup);
    bytes.insert(std::end(bytes), std::begin(joined), std::end(joined));
  }

  if (!skipSignature && !this->signature.empty()) {
    const auto signatureBytes = HexToBytes(this->signature.c_str());
    bytes.insert(std::end(bytes), std::begin(signatureBytes), std::end(signatureBytes));
  }

  if (!skipSecondSignature && !this->secondSignature.empty()) {
    const auto secondSignatureBytes = HexToBytes(this->secondSignature.c_str());
    bytes.insert(std::end(bytes), std::begin(secondSignatureBytes), std::end(secondSignatureBytes));
  }

  return bytes;
}

std::map<std::string, std::string> Ark::Crypto::Transactions::Transaction::toArray() {
  //  buffers for variable and non-string type-values.
  char amount[24], assetName[16], assetValue[512], fee[24], network[8], signatures[512], timestamp[36], type[8], version[8];

  //  Amount
  sprintf(amount, "%llu", this->amount);

  //  Asset
  if (this->type == 0) {  //  Transfer
    //do nothing
  } else if (this->type == 1) { //  Second Signature Registration

    strcpy(assetName, "publicKey");
    strcpy(assetValue, this->asset.signature.publicKey.c_str());

  } else if (this->type == 2) { //  Delegate Registration

    strcpy(assetName, "username");
    strcpy(assetValue, this->asset.delegate.username.c_str());

  } else if (this->type == 3) {  //  Vote

    strcpy(assetName, "votes");
    strcpy(assetValue, "");
    for (unsigned int i = 0; i < this->asset.votes.size(); ++i) {
      strcat(assetValue, this->asset.votes[i].c_str());

      if (i < this->asset.votes.size() - 1) {
        strcat(assetValue, ",");
      }
    }

  // } else if (this->type == 4) {  //  Multisignature Registration
  //   //  TODO
  // } else if (this->type == 5) {  //  IPFS
  //   //  TBD
  // } else if (this->type == 6) {  //  Timelock Registration
  //   //  TBD
  // } else if (this->type == 7) {  //  Multi-Payment
  //   //  TBD
  // } else if (this->type == 8) {  //  Delegate Resignation
  //   //  TBD
  };

  //  Fee
  // fee << this->fee;
  sprintf(fee, "%llu",  this->fee);

  //  Signatures
  strcpy(signatures, "");
  for (unsigned int i = 0; i < this->signatures.size(); ++i) {
    strcat(signatures, this->signatures[i].c_str());
    if (i < this->signatures.size() - 1) {
      strcpy(signatures, ",");
    }
  }

  //  Network
  sprintf(network, "%d", this->network);

  //  Timestamp
  sprintf(timestamp, "%d", this->timestamp);

  //  Type
  sprintf(type, "%d", this->type);

  //  Version
  sprintf(version, "%d", this->version);

  return {
    {"amount", amount},
    {assetName, assetValue},
    {"fee", fee},
    {"id", this->id},
    {"network", network},
    {"recipientId", this->recipientId},
    {"secondSignature", this->secondSignature},
    {"senderPublicKey", this->senderPublicKey},
    {"signature", this->signature},
    {"signatures", signatures},
    {"signSignature", this->signSignature},
    {"timestamp", timestamp},
    {"type", type},
    {"vendorField", this->vendorField},
    {"version", version}
  };
}

std::string Ark::Crypto::Transactions::Transaction::toJson() {
  std::map<std::string, std::string> txArray = this->toArray();

  const size_t capacity = JSON_OBJECT_SIZE(15);
  DynamicJsonBuffer jsonBuffer(capacity);

  JsonObject& root = jsonBuffer.createObject();

  //  Amount
  root["amount"] = strtoull(txArray["amount"].c_str(), nullptr, 10);

  //  Asset
  if (this->type == 0) {  //  Transfer
    //do nothing
  } else if (this->type == 1) { //  Second Signature Registration

    JsonObject& tAsset = root.createNestedObject("asset");
    JsonObject& signature = tAsset.createNestedObject("signature");
    signature["publicKey"] = txArray["publicKey"];

  } else if (this->type == 2) { //  Delegate Registration

    JsonObject& dAsset = root.createNestedObject("asset");
    JsonObject& delegate = dAsset.createNestedObject("delegate");
    delegate["username"] = txArray["username"];

  }else if (this->type == 3) {  //  Vote

    JsonObject& vAsset = root.createNestedObject("asset");
    JsonArray& votes = vAsset.createNestedArray("votes");

    std::string::size_type lastPos = txArray["votes"].find_first_not_of(",", 0);
    std::string::size_type pos = txArray["votes"].find_first_of(",", lastPos);
    while (std::string::npos != pos || std::string::npos != lastPos) {
      votes.add(txArray["votes"].substr(lastPos, pos - lastPos));
      lastPos = txArray["votes"].find_first_not_of(",", pos);
      pos = txArray["votes"].find_first_of(",", lastPos);
    }

  // } else if (this->type == 4) {  //  Multisignature Registration
  //   //  TODO
  // } else if (this->type == 5) {  //  IPFS
  //   //  TBD
  // } else if (this->type == 6) {  //  Timelock Registration
  //   //  TBD
  // } else if (this->type == 7) {  //  Multi-Payment
  //   //  TBD
  // } else if (this->type == 8) {  //  Delegate Resignation
  //   //  TBD
  };

  //  Fee
  root["fee"] = strtoull(txArray["fee"].c_str(), nullptr, 10);

  //  Id
  root["id"] = txArray["id"];

  //  Network
  if (txArray["network"] != "0") {
    root["network"] = atoi(txArray["network"].c_str());
  }

  //  RecipientId
  root["recipientId"] = txArray["recipientId"];

  //  SecondSignature
  if (std::strlen(txArray["secondSignature"].c_str()) > 0) {
    root["secondSignature"] = txArray["secondSignature"];
  }

  //  SenderPublicKey
  root["senderPublicKey"] = txArray["senderPublicKey"];

  //  Signature
  root["signature"] = txArray["signature"];

  //  Signatures
  if (this->signatures.size() > 0) {
    JsonArray& signatures = root.createNestedArray("signatures");
    std::string::size_type lastPos = txArray["signatures"].find_first_not_of(",", 0);
    std::string::size_type pos = txArray["signatures"].find_first_of(",", lastPos);
    while (std::string::npos != pos || std::string::npos != lastPos) {
      signatures.add(txArray["signatures"].substr(lastPos, pos - lastPos));
      lastPos = txArray["signatures"].find_first_not_of(",", pos);
      pos = txArray["signatures"].find_first_of(",", lastPos);
    }
  }

  //  SignSignature
  if (std::strlen(txArray["signSignature"].c_str()) > 0) {
    root["signSignature"] = txArray["signSignature"];
  }

  //  Timestamp
  root["timestamp"] = strtoul(txArray["timestamp"].c_str(), nullptr, 10);

  //  Type
  root["type"] = atoi(txArray["type"].c_str());

  //  VendorField
  if (std::strlen(txArray["vendorField"].c_str()) > 0) {
    root["vendorField"] = txArray["vendorField"];
  }

  //  Version
  if (txArray["version"] != "0") {
    root["version"] = atoi(txArray["version"].c_str());
  };

  char jsonChar[root.measureLength() + 1];
  root.printTo((char*)jsonChar, sizeof(jsonChar));

  return jsonChar;
}
sleepdefic1t commented 5 years ago

Resolved in #103