Closed sleepdefic1t closed 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;
}
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.
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 ✌️
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;
}
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;
}
Resolved in #103
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
andstoi
may also be required per userAsmita
.Steps to Reproduce (for bugs)
transaction
object..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