blob: ede38968b7eabf1ce1c96e04ba806e414271028a [file]
#include "sdk_transaction.h"
#include <cryptopp/files.h>
#include <cryptopp/hex.h>
#include <cryptopp/sha3.h>
#include <nlohmann/json.hpp>
#include <iostream>
#include <stdexcept>
SDKTransaction::SDKTransaction(rapidjson::Document& tx_body, bool skip_schema_validation) {
if (!skip_schema_validation) {
ValidateId(tx_body);
ValidateOperation(tx_body);
}
// clears the fields of tx_body parameter and moves them to doc_
doc_.SetObject();
doc_.AddMember("id", tx_body["id"], doc_.GetAllocator());
// std::cout<< "added id" << std::endl;
doc_.AddMember("operation", tx_body["operation"], doc_.GetAllocator());
// std::cout << "added operation" << std::endl;
doc_.AddMember("inputs", tx_body["inputs"], doc_.GetAllocator());
// std::cout << "added inputs" << std::endl;
doc_.AddMember("outputs", tx_body["outputs"], doc_.GetAllocator());
// std::cout << "added outputs" << std::endl;
doc_.AddMember("metadata", tx_body["metadata"], doc_.GetAllocator());
// std::cout << "added metadata" << std::endl;
doc_.AddMember("asset", tx_body["asset"], doc_.GetAllocator());
// std::cout << "added asset" << std::endl;
}
// TODO: find better exceptions
void SDKTransaction::Validate(std::unordered_map<std::string, SDKTransaction&>& current_transactions) {
std::string operation = doc_["operation"].GetString();
if (operation == "CREATE") {
std::string id = doc_["id"].GetString();
// check for txn with same id in current_transactions
if (current_transactions.find(id) != current_transactions.end()) {
throw std::invalid_argument("Transaction with same id already found in current_transactions");
}
if (IsCommitted(id)) {
throw std::invalid_argument("Transaction with same id already committed to NexRes");
}
if (!InputsValid(nullptr)) { // pass empty array
throw std::invalid_argument("Invalid inputs");
}
}
else if (operation == "TRANSFER") {
ValidateTransferInputs(current_transactions);
}
}
// TODO: find better exceptions
// Validates the Inputs in the Transaction against given Outputs
bool SDKTransaction::InputsValid(rapidjson::Value::Array* outputs) {
std::string operation = doc_["operation"].GetString();
if (operation == "CREATE") {
std::vector<std::string> dummy_outputs;
for (unsigned int i = 0; i < doc_["inputs"].GetArray().Size(); i++) {
dummy_outputs.push_back("dummy_value");
}
return InputsValid2(dummy_outputs);
}
else if (operation == "TRANSFER") {
std::vector<std::string> output_condition_uris;
if (outputs) {
for (auto& output : *outputs) {
output_condition_uris.push_back(output["condition"]["uri"].GetString());
}
}
return InputsValid2(output_condition_uris);
}
else {
throw std::invalid_argument("Invalid operation type");
return false;
}
}
// TODO
// Validates the Inputs in the Transaction against given Outputs
// Note: The number of `output_condition_uris` must be equal to the number of Inputs a Transaction has.
bool SDKTransaction::InputsValid2(std::vector<std::string> output_condition_uris) {
return true;
}
std::string SDKTransaction::SerializeMessage(std::string& tx) {
nlohmann::json j;
j = nlohmann::json::parse(tx); // sort keys
std::string str = j.dump(-1, ' ', false); // don't indent, don't ensure ascii
return str;
}
std::string SDKTransaction::HashData(std::string& data) {
CryptoPP::SHA3_256 hash;
hash.Update((const CryptoPP::byte*) data.data(), data.size());
std::string digest;
digest.resize(hash.DigestSize());
hash.Final((CryptoPP::byte*)&digest[0]);
std::string encoded;
CryptoPP::StringSource ss((const CryptoPP::byte*) digest.data(), digest.size(), true,
new CryptoPP::HexEncoder(
new CryptoPP::StringSink(encoded)
)
);
return encoded;
}
// TODO: find better exceptions
void SDKTransaction::ValidateId(rapidjson::Document& tx_body) {
rapidjson::Document tx_body_copy;
tx_body_copy.CopyFrom(tx_body, tx_body_copy.GetAllocator());
if (!tx_body_copy.HasMember("id")) {
throw std::invalid_argument("tx_body has no member \"id\"");
}
std::string proposed_tx_id = tx_body_copy["id"].GetString();
// set id to null
tx_body_copy.AddMember("id", rapidjson::Value(), tx_body_copy.GetAllocator());
// create string of serialized transaction
rapidjson::StringBuffer buffer;
buffer.Clear();
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
tx_body_copy.Accept(writer);
std::string tx_body_str = buffer.GetString();
std::string tx_body_serialized_str = SerializeMessage(tx_body_str);
std::string valid_tx_id = HashData(tx_body_serialized_str);
if (proposed_tx_id != valid_tx_id) {
throw std::invalid_argument("proposed tx id differs from valid tx id");
}
}
// TODO
bool SDKTransaction::IsCommitted(std::string& tx_id) {
// use storage pointer?
return true;
}
void SDKTransaction::ValidateOperation(rapidjson::Document& tx_body) {
std::string operation = tx_body["operation"].GetString();
if (operation != "CREATE" && operation != "TRANSFER") {
throw std::invalid_argument("Invalid operation type");
}
}
// TODO
void SDKTransaction::ValidateTransferInputs(std::unordered_map<std::string, SDKTransaction&>& current_transactions) {
;
}