sleepdefic1t
11/29/2019 - 11:18 PM

Radians C++ Scooter Registration Implementation

Radians C++ Scooter Registration Implementation


cmake_minimum_required(VERSION 3.2)

project(ark_cpp_crypto C CXX)

# ------------------------------------------------------------------------------
# External Libraries
# ------------------------------------------------------------------------------

include(${CMAKE_SOURCE_DIR}/cmake/External.cmake)

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Internal Libraries
# ------------------------------------------------------------------------------

set(INTERNAL_LIBRARY_SOURCE_DIRS
    ${PROJECT_SOURCE_DIR}/lib/bcl
    ${PROJECT_SOURCE_DIR}/lib/rfc6979
)

include_directories(${INTERNAL_LIBRARY_SOURCE_DIRS})

# ------------------------------------------------------------------------------

# Nayuki's Bitcoin Cryptography Library
set(BCL_SOURCE
    lib/bcl/Base58Check.cpp
    lib/bcl/CurvePoint.cpp
    lib/bcl/Ecdsa.cpp
    lib/bcl/FieldInt.cpp
    lib/bcl/Ripemd160.cpp
    lib/bcl/Sha256Hash.cpp
    lib/bcl/Sha256.cpp
    lib/bcl/Sha512.cpp
    lib/bcl/Uint256.cpp
    lib/bcl/Utils.cpp
)

set(INTERNAL_LIBRARY_SOURCE ${BCL_SOURCE})

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# ARK C++ Crypto Source
# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Common

set(COMMON_SOURCE
    common/configuration.cpp
    common/network.cpp
)

# ------------------------------------------------------------------------------
# Crypto

set(CRYPTO_SOURCE
    crypto/curve.cpp
    crypto/hash.cpp
    crypto/message.cpp
    crypto/slot.cpp
)

# ------------------------------------------------------------------------------
# Identities

set(IDENTITIES_SOURCE
    identities/address.cpp
    identities/keys.cpp
    identities/privatekey.cpp
    identities/publickey.cpp
    identities/wif.cpp
)

# ------------------------------------------------------------------------------
# Transactions

# Types
set(TRANSACTIONS_TYPES_SOURCE
    transactions/types/transfer.cpp
    transactions/types/second_signature.cpp
    transactions/types/delegate_registration.cpp
    transactions/types/vote.cpp
    # transactions/types/multi_signature.cpp
    transactions/types/ipfs.cpp
    transactions/types/multi_payment.cpp
    transactions/types/htlc_lock.cpp
    transactions/types/htlc_claim.cpp
    transactions/types/htlc_refund.cpp
    transactions/types/scooter_registration.cpp
)

# Serialization/Deserialization
set(TRANSACTIONS_SERDE_SOURCE
    transactions/deserializer.cpp
    transactions/serializer.cpp
)

# Transaction Source Files
set(TRANSACTIONS_SOURCE
    ${TRANSACTIONS_TYPES_SOURCE}
    ${TRANSACTIONS_SERDE_SOURCE}
    transactions/transaction.cpp
)

# ------------------------------------------------------------------------------
# Utils

set(UTILS_SOURCE utils/base58.cpp)

# ------------------------------------------------------------------------------
# ARK C++ Crypto Library Source

set(ARK_LIBRARY_SOURCE
    ${COMMON_SOURCE}
    ${CRYPTO_SOURCE}
    ${IDENTITIES_SOURCE}
    ${TRANSACTIONS_SOURCE}
    ${UTILS_SOURCE}
)

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Add the Source Files to the Library
# ------------------------------------------------------------------------------

add_library(${PROJECT_NAME} STATIC
            ${EXTERNAL_LIBRARY_SOURCE}
            ${INTERNAL_LIBRARY_SOURCE}
            ${ARK_LIBRARY_SOURCE}
)

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Set the Include Folders
# ------------------------------------------------------------------------------

set(ARK_CPP_CRYPTO_INCLUDE_DIRS
    ${PROJECT_SOURCE_DIR}
    ${PROJECT_SOURCE_DIR}/include/cpp-crypto
    ${PROJECT_SOURCE_DIR}/lib/
)

include_directories(${ARK_CPP_CRYPTO_INCLUDE_DIRS})

target_include_directories(${PROJECT_NAME} PUBLIC ${ARK_CPP_CRYPTO_INCLUDE_DIRS})

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Windows: Link to `crypt32`
# ------------------------------------------------------------------------------

if (MSVC)
    target_link_libraries(${PROJECT_NAME} PUBLIC crypt32)
endif()

# ------------------------------------------------------------------------------
/**
 * 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.
 **/

#ifndef ARK_TRANSACTIONS_BUILDERS_SCOOTER_REGISTRATION_HPP
#define ARK_TRANSACTIONS_BUILDERS_SCOOTER_REGISTRATION_HPP

#include <string>

#include "transactions/builders/common.hpp"

namespace Ark {
namespace Crypto {
namespace transactions {
namespace builder {

////////////////////////////////////////////////////////////////////////////////
// Forward Declaration
class ScooterRegistration;

////////////////////////////////////////////////////////////////////////////////
class ScooterRegistration : public Common<ScooterRegistration> {
  public:
    ////////////////////////////////////////////////////////////////////////////
    // Amount
    ScooterRegistration &scooterId(const std::string &scooterId) {
        this->transaction.data.asset.scooterRegistration.scooterId = scooterId;
        return *this;
    }
};

}  // namespace builder
}  // namespace transactions
}  // namespace Crypto
}  // namespace Ark

#endif
/**
 * 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 "transactions/deserializer.hpp"

#include <cstdbool>
#include <cstdint>
#include <utility>
#include <vector>

#include "interfaces/constants.h"

#include "transactions/defaults/offsets.hpp"

#include "transactions/transaction_data.hpp"

#include "utils/unpack.h"

namespace Ark {
namespace Crypto {
namespace transactions {

////////////////////////////////////////////////////////////////////////////////
// Deserialize Common
//
// @param TransactionData *data: The Transaction Data destination.
// @param const std::vector<uint8_t> &buffer
//
// ---
// Internals:
//
// Header - 1 Byte:
// - data->header = buffer.at(0);
//
// Transaction Version - 1 Byte:
// - data->version = buffer.at(1);
//
// Network Version - 1 Byte:
// - data->network = buffer.at(2);
//
// TypeGroup - 4 Bytes:
// - data->typeGroup = unpack4LE(&buffer, 3); 
//
// Transaction Type - 2 Bytes:
// - data->type = unpack2LE(&buffer, 7);
//
// Nonce - 8 Bytes:
// - data->nonce = unpack8LE(&buffer, 9);
//
// SenderPublicKey - 33 Bytes:
// - std::move(&buffer.at(17), &buffer.at(50), data->senderPublicKey.begin());
//
// Fee - 8 bytes
// - data->fee = unpack8LE(&buffer, 50);
//
// VendorField Length - 1 Byte:
// - buffer.at(58);
//
// VendorField - 0 - 255 Bytes:
// - data->vendorField.insert(data->vendorField.begin(), &buffer.at(59), &buffer.at(59 + data->vendorField.size()));
//
// ---
static void deserializeCommon(TransactionData *data,
                              const std::vector<uint8_t> &buffer) {
    data->header        = buffer.at(HEADER_OFFSET);                 // 1 Byte
    data->version       = buffer.at(VERSION_OFFSET);                // 1 Byte
    data->network       = buffer.at(NETWORK_OFFSET);                // 1 Byte

    data->typeGroup     = unpack4LE(buffer, TYPEGROUP_OFFSET);      // 4 Bytes
    data->type          = unpack2LE(buffer, TYPE_OFFSET);           // 2 Bytes
    data->nonce         = unpack8LE(buffer, NONCE_OFFSET);          // 8 Bytes

    std::move(&buffer.at(SENDER_PUBLICKEY_OFFSET),                  // 21 Bytes
              &buffer.at(SENDER_PUBLICKEY_OFFSET + PUBLICKEY_COMPRESSED_LEN),
              data->senderPublicKey.begin());

    data->fee           = unpack8LE(buffer, FEE_OFFSET);           // 8 Bytes

    if (buffer.at(VF_LEN_OFFSET) != 0U) {
        data->vendorField.insert(                           // 0 <=> 255 Bytes
                data->vendorField.begin(),
                &buffer.at(VF_OFFSET),
                &buffer.at(VF_OFFSET + buffer.at(VF_LEN_OFFSET)));
    };
}

////////////////////////////////////////////////////////////////////////////////
// Deserialize Common v1
//
// @param TransactionData *data: The Transaction Data destination.
// @param const std::vector<uint8_t> &buffer
//
// ---
// Internals:
//
// Header - 1 Byte:
// - data->header = buffer.at(0);
//
// Transaction Version - 1 Byte:
// - data->version = buffer.at(1);
//
// Network Version - 1 Byte:
// - data->network = buffer.at(2);
//
// Transaction Type - 1 Byte:
// - data->type = buffer.at(3);
//
// Timestamp - 4 Bytes
// - data->timestamp = unpack4LE(buffer, 4);
//
// SenderPublicKey - 33 Bytes:
// - std::move(&buffer.at(8), &buffer.at(41) data->senderPublicKey.begin());
//
// Fee - 8 bytes
// - data->fee = unpack8LE(buffer, 41);
//
// VendorField Length - 1 Byte:
// - buffer.at(49)
//
// VendorField - 0 - 255 Bytes:
// - data->vendorField.insert(data->vendorField.begin(), &buffer.at(50), &buffer.at(50 + buffer.at(49)));
//
// ---
static void deserializeCommonV1(TransactionData *data,
                                const std::vector<uint8_t> &buffer) {
    data->header        = buffer.at(HEADER_OFFSET);                 // 1 Byte
    data->version       = buffer.at(VERSION_OFFSET);                // 1 Byte
    data->network       = buffer.at(NETWORK_OFFSET);                // 1 Byte

    data->type          = buffer.at(v1::TYPE_OFFSET);               // 1 Bytes

    data->timestamp     = unpack4LE(buffer, v1::TIMESTAMP_OFFSET);  // 4 Bytes

    std::move(&buffer.at(v1::SENDER_PUBLICKEY_OFFSET),              // 33 Bytes
              &buffer.at(v1::SENDER_PUBLICKEY_OFFSET + PUBLICKEY_COMPRESSED_LEN),
              data->senderPublicKey.begin());

    data->fee        = unpack8LE(buffer, v1::FEE_OFFSET);           // 8 Bytes

    if (buffer.at(v1::VF_LEN_OFFSET) != 0U) {
        data->vendorField.insert(                           // 0 <=> 255 Bytes
                data->vendorField.begin(),
                &buffer.at(v1::VF_OFFSET),
                &buffer.at(v1::VF_OFFSET + buffer.at(v1::VF_LEN_OFFSET)));
    };
}

////////////////////////////////////////////////////////////////////////////////
static auto deserializeAsset(TransactionData *transaction,
                             const std::vector<uint8_t> &buffer,
                             const size_t offset) -> size_t {
    switch (transaction->type) {
        case TRANSFER_TYPE:
            return Transfer::Deserialize(
                    &transaction->asset.transfer,
                    &buffer.at(offset));

        case SECOND_SIGNATURE_TYPE:
            return SecondSignature::Deserialize(
                    &transaction->asset.secondSignature,
                    &buffer.at(offset));

        case DELEGATE_REGISTRATION_TYPE:
            return DelegateRegistration::Deserialize(
                    &transaction->asset.delegateRegistration,
                    &buffer.at(offset));

        case VOTE_TYPE:
            return Vote::Deserialize(
                    &transaction->asset.vote,
                    &buffer.at(offset));

        // case MULTI_SIGNATURE_TYPE:  // TODO

        case IPFS_TYPE:
            return Ipfs::Deserialize(
                    &transaction->asset.ipfs,
                    &buffer.at(offset));

        case MULTI_PAYMENT_TYPE:
            return MultiPayment::Deserialize(
                    &transaction->asset.multiPayment,
                    &buffer.at(offset));

        case DELEGATE_RESIGNATION_TYPE: return 0UL;

        case HTLC_LOCK_TYPE:
            return HtlcLock::Deserialize(
                    &transaction->asset.htlcLock,
                    &buffer.at(offset));
        
        case HTLC_CLAIM_TYPE:
            return HtlcClaim::Deserialize(
                    &transaction->asset.htlcClaim,
                    &buffer.at(offset));

        case HTLC_REFUND_TYPE:
            return HtlcRefund::Deserialize(
                    &transaction->asset.htlcRefund,
                    &buffer.at(offset));

        case SCOOTER_TYPE:
            if (transaction->typeGroup == SCOOTER_TYPEGROUP) {
                return ScooterRegistration::Deserialize(
                        &transaction->asset.scooterRegistration,
                        &buffer.at(offset));
            }

        default: return 0UL;
    };
}

////////////////////////////////////////////////////////////////////////////////
static void deserializeSignatures(TransactionData *transaction,
                                  const uint8_t *buffer) {
    uint8_t signatureLength = buffer[1] + 2U;
    if (signatureLength >= SIGNATURE_ECDSA_MIN &&
        signatureLength <= SIGNATURE_ECDSA_MAX) {
        transaction->signature.reserve(SIGNATURE_ECDSA_MAX);
        transaction->signature.insert(transaction->signature.begin(),
                                      buffer,
                                      buffer + signatureLength);
    }

    uint8_t secondSignatureLength = buffer[signatureLength + 1U] + 2U;
    if (secondSignatureLength >= SIGNATURE_ECDSA_MIN &&
        secondSignatureLength <= SIGNATURE_ECDSA_MAX) {
        transaction->secondSignature.reserve(SIGNATURE_ECDSA_MAX);
        transaction->secondSignature.insert(
                transaction->secondSignature.begin(),
                buffer + signatureLength,
                buffer + signatureLength + secondSignatureLength);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Deserialize Transaction Data
//
// @param TransactionData *data
// @param const std::vector<uint8_t> &buffer
//
// ---
auto Deserializer::deserialize(TransactionData *data,
                               const std::vector<uint8_t> &buffer) -> bool {
    size_t assetOffset = 0UL;

    // Use v2 or v1, otherwise return with no changes to the Tx Data.
    if (buffer.at(VERSION_OFFSET) == 0x02) {
        deserializeCommon(data, buffer);
        assetOffset = VF_OFFSET + data->vendorField.size();
    }
    else if(buffer.at(VERSION_OFFSET) == 0x01) {
        deserializeCommonV1(data, buffer);
        assetOffset = v1::VF_OFFSET + data->vendorField.size();
    }
    else { return false; }

    size_t assetSize = deserializeAsset(data, buffer, assetOffset);

    deserializeSignatures(data, &buffer[assetOffset + assetSize]);

    return true;
}

}  // namespace transactions
}  // namespace Crypto
}  // namespace Ark
/**
 * 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 "transactions/serializer.hpp"

#include <cstdint>
#include <cstring>
#include <vector>

#include "interfaces/constants.h"

#include "crypto/hash.hpp"

#include "transactions/defaults/offsets.hpp"

#include "transactions/transaction_data.hpp"

namespace Ark {
namespace Crypto {
namespace transactions {

////////////////////////////////////////////////////////////////////////////////
// Serialize Common
//
// @param const TransactionData &data
// @param std::vector<uint8_t> &buffer: The serialized transactions buffer.
//
// ---
// Internals:
//
// Header - 1 Byte:
// - buffer.at(0) = transaction.header;
//
// Transaction Version - 1 Byte:
// - buffer.at(1) = transaction.version;
//
// Network Version - 1 Byte:
// - buffer.at(2) = transaction.network;
//
// TypeGroup - 4 Bytes:
// - memmove(&buffer.at(3), &transaction.typeGroup, sizeof(uint32_t));
//
// Transaction Type - 2 Bytes:
// - memmove(&buffer.at(7), &transaction.type, sizeof(uint16_t));
//
// Nonce - 8 Bytes:
// - memmove(&buffer.at(9), &transaction.nonce, sizeof(uint64_t));
//
// SenderPublicKey - 33 Bytes:
// - buffer.insert(buffer.begin() + 17, transaction.senderPublicKey.begin(), transaction.senderPublicKey.end());
//
// Fee - 8 bytes
// - memmove(&buffer.at(50), &transaction.fee, sizeof(uint64_t));
//
// VendorField Length - 1 Byte:
// - buffer.at(58) = transaction.vendorField.size();
//
// VendorField - 0 <=> 255 Bytes:
// - buffer.insert(buffer.begin() + 59, transaction.vendorField.begin(), transaction.vendorField.end());
//
// ---
static void serializeCommon(const TransactionData &transaction,
                            std::vector<uint8_t> &buffer) {
    buffer.at(HEADER_OFFSET)        = transaction.header;           // 1 Byte
    buffer.at(VERSION_OFFSET)       = transaction.version;          // 1 Byte
    buffer.at(NETWORK_OFFSET)       = transaction.network;          // 1 Byte

    memmove(&buffer.at(TYPEGROUP_OFFSET),                           // 4 Bytes
            &transaction.typeGroup,
            sizeof(uint32_t));

    memmove(&buffer.at(TYPE_OFFSET),                                // 2 Bytes
            &transaction.type,
            sizeof(uint16_t));

    memmove(&buffer.at(NONCE_OFFSET),                               // 4 Bytes
            &transaction.nonce,
            sizeof(uint64_t));

    buffer.insert(buffer.begin() + SENDER_PUBLICKEY_OFFSET,         // 21 Bytes
                  transaction.senderPublicKey.begin(),
                  transaction.senderPublicKey.end());

    memmove(&buffer.at(FEE_OFFSET),                                 // 8 Bytes
            &transaction.fee,
            sizeof(uint64_t));

    buffer.at(VF_LEN_OFFSET) =                                      // 1 Byte
            static_cast<uint8_t>(transaction.vendorField.size());

    if (!transaction.vendorField.empty()) {
        buffer.insert(buffer.begin() + VF_OFFSET,           // 0 <=> 255 Bytes
                     transaction.vendorField.begin(),
                     transaction.vendorField.end());
    }
}

////////////////////////////////////////////////////////////////////////////////
// Serialize Common V1
//
// @param const TransactionData &data
// @param std::vector<uint8_t> &buffer: The serialized transactions buffer.
//
// ---
// Internals:
//
// Header - 1 Byte:
// - buffer.at(0) = transaction.header;
//
// Transaction Version - 1 Byte:
// - buffer.at(1) = transaction.version;
//
// Network Version - 1 Byte:
// - buffer.at(2) = transaction.network;
//
// Transaction Type - 1 Byte:
// - buffer.at(3) = transaction.type;
//
// Timestamp - 4 Bytes
// - memmove(&buffer.at(4), &transaction.timestamp,sizeof(uint32_t));
//
// SenderPublicKey - 33 Bytes:
// - buffer.insert(buffer.begin() + 8, transaction.senderPublicKey.begin(), transaction.senderPublicKey.end());
//
// Fee - 8 bytes
// - memmove(&buffer.at(41), &transaction.fee, sizeof(uint64_t));
//
// VendorField Length - 1 Byte:
// - buffer.at(49) = transaction.vendorField.size();
//
// VendorField - 0 <=> 255 Bytes:
// - buffer.insert(buffer.begin() + 50, transaction.vendorField.begin(), transaction.vendorField.end());
//
// ---
static void serializeCommonV1(const TransactionData &transaction,
                              std::vector<uint8_t> &buffer) {
    buffer.at(v1::HEADER_OFFSET)        = transaction.header;       // 1 Byte
    buffer.at(v1::VERSION_OFFSET)       = transaction.version;      // 1 Byte
    buffer.at(v1::NETWORK_OFFSET)       = transaction.network;      // 1 Byte

    buffer.at(v1::TYPE_OFFSET) =                                    // 1 Byte
            static_cast<uint8_t>(transaction.type);

    memmove(&buffer.at(v1::TIMESTAMP_OFFSET),                       // 4 Bytes
            &transaction.timestamp,
            sizeof(uint32_t));

    buffer.insert(buffer.begin() + v1::SENDER_PUBLICKEY_OFFSET,     // 21 Bytes
                  transaction.senderPublicKey.begin(),
                  transaction.senderPublicKey.end());

    memmove(&buffer.at(v1::FEE_OFFSET),                             // 8 Bytes
            &transaction.fee,
            sizeof(uint64_t));

    buffer.at(v1::VF_LEN_OFFSET) =                                  // 1 Byte
            static_cast<uint8_t>(transaction.vendorField.size());

    if (!transaction.vendorField.empty()) {
        buffer.insert(buffer.begin() + v1::VF_OFFSET,       // 0 <=> 255 Bytes
                      transaction.vendorField.begin(),
                      transaction.vendorField.end());
    }
}

////////////////////////////////////////////////////////////////////////////////
static auto serializeAsset(const TransactionData &transaction,
                           std::vector<uint8_t> &buffer,
                           const size_t offset) -> size_t {
    switch (transaction.type) {
        case TRANSFER_TYPE:
            return Transfer::Serialize(
                    transaction.asset.transfer,
                    &buffer.at(offset));

        case SECOND_SIGNATURE_TYPE:
            return SecondSignature::Serialize(
                    transaction.asset.secondSignature,
                    &buffer.at(offset));

        case DELEGATE_REGISTRATION_TYPE:
            return DelegateRegistration::Serialize(
                    transaction.asset.delegateRegistration,
                     &buffer.at(offset));

        case VOTE_TYPE:
            return Vote::Serialize(
                    transaction.asset.vote,
                    &buffer.at(offset));

        // case MULTI_SIGNATURE_TYPE:  // TODO

        case IPFS_TYPE:
            return Ipfs::Serialize(
                    transaction.asset.ipfs,
                    &buffer.at(offset));

        case MULTI_PAYMENT_TYPE:
            return MultiPayment::Serialize(
                    transaction.asset.multiPayment,
                    buffer,
                    offset);

        case DELEGATE_RESIGNATION_TYPE: return 0UL;

        case HTLC_LOCK_TYPE:
            return HtlcLock::Serialize(
                    transaction.asset.htlcLock,
                    &buffer.at(offset));

        case HTLC_CLAIM_TYPE:
            return HtlcClaim::Serialize(
                    transaction.asset.htlcClaim,
                    &buffer.at(offset));

        case HTLC_REFUND_TYPE:
            return HtlcRefund::Serialize(
                    transaction.asset.htlcRefund,
                    &buffer.at(offset));

        case SCOOTER_TYPE:
            if (transaction.typeGroup == SCOOTER_TYPEGROUP) {
                return ScooterRegistration::Serialize(
                        transaction.asset.scooterRegistration,
                        &buffer.at(offset));
            }

        default: return 0UL;
    };
}

////////////////////////////////////////////////////////////////////////////////
static auto serializeSignatures(const TransactionData &data,
                                std::vector<uint8_t> &buffer,
                                const size_t offset,
                                const SerializerOptions &options) -> size_t {
    if (!options.excludeSignature &&
        data.signature.size() >= SIGNATURE_ECDSA_MIN &&
        data.signature.size() <= SIGNATURE_ECDSA_MAX) {
        buffer.insert(buffer.begin() + offset,
                      data.signature.begin(),
                      data.signature.end());
    }
    else { return 0UL; }

    if (!options.excludeSecondSignature &&
        data.secondSignature.size() >= SIGNATURE_ECDSA_MIN &&
        data.secondSignature.size() <= SIGNATURE_ECDSA_MAX) {
        buffer.insert(buffer.begin() + offset + data.signature.size(),
                      data.secondSignature.begin(),
                      data.secondSignature.end());
    }
    else { return data.signature.size(); }

    return data.signature.size() + data.secondSignature.size();
}

////////////////////////////////////////////////////////////////////////////////
// Serialize Transaction Data
//
// @param const TransactionData &data
// @param const SerializerOptions &options
//
// @return std::vector<uint8_t>
//
// ---
auto Serializer::serialize(const TransactionData &data,
                           const SerializerOptions &options)
                                    -> std::vector<uint8_t>{
    std::vector<uint8_t> buffer;
    buffer.resize(TX_DEFAULT_SIZE);

    size_t assetOffset = 0UL;

    // Use v2 or v1, otherwise return an empty object.
    if (data.version == TRANSACTION_VERSION_TYPE_2) {
        serializeCommon(data, buffer);
        assetOffset = VF_OFFSET + data.vendorField.size();
    }
    else if (data.version == TRANSACTION_VERSION_TYPE_1) {
        serializeCommonV1(data, buffer);
        assetOffset = v1::VF_OFFSET + data.vendorField.size();
    }
    else { return {}; }

    size_t assetSize = serializeAsset(data, buffer, assetOffset);

    size_t signaturesSize = serializeSignatures(data,
                                                buffer,
                                                assetOffset + assetSize,
                                                options);

    buffer.resize(assetOffset + assetSize + signaturesSize);

    return buffer;
}

}  // namespace transactions
}  // namespace Crypto
}  // namespace Ark
/**
 * 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 "transactions/transaction.hpp"

#include <cstddef>
#include <cstdint>
#include <map>
#include <string>
#include <utility>
#include <vector>

#include "crypto/curve.hpp"
#include "crypto/hash.hpp"

#include "identities/keys.hpp"

#include "transactions/deserializer.hpp"
#include "transactions/serializer.hpp"

#include "transactions/defaults/offsets.hpp"

#include "transactions/transaction_data.hpp"

#include "utils/base58.hpp"
#include "utils/hex.hpp"
#include "utils/json.h"
#include "utils/str.hpp"

namespace Ark {
namespace Crypto {
namespace transactions {

////////////////////////////////////////////////////////////////////////////////
// Compute the unique transaction ID.
auto Transaction::getId() const -> Hash32 {
    const auto serialized = this->toBytes(false, false);
    return Hash::sha256(serialized.data(), serialized.size());
}

////////////////////////////////////////////////////////////////////////////////
// Sign the transaction using a passphrase.
auto Transaction::sign(const std::string &passphrase) -> bool {
    if (passphrase.empty()) {
        return false;
    }

    const auto keys = identities::Keys::fromPassphrase(passphrase.c_str());

    std::move(keys.publicKey.begin(),
              keys.publicKey.end(),
              this->data.senderPublicKey.begin());

    const auto serialized = this->toBytes(true, true);
    const auto hash32 = Hash::sha256(serialized.data(), serialized.size());

    this->data.signature.reserve(SIGNATURE_ECDSA_MAX);

    return Curve::Ecdsa::sign(hash32.data(),
                              keys.privateKey.data(),
                              &this->data.signature);
}

////////////////////////////////////////////////////////////////////////////////
// Sign the Transaction using a Second Passphrase.
auto Transaction::secondSign(const std::string &secondPassphrase) -> bool {
    if (this->data.signature.empty() || secondPassphrase.empty()) {
        return false;
    }

    const auto keys = identities::Keys::fromPassphrase(secondPassphrase.c_str());

    const auto serialized = this->toBytes(false, true);
    const auto hash32 = Hash::sha256(serialized.data(), serialized.size());

    this->data.secondSignature.reserve(SIGNATURE_ECDSA_MAX);
    
    return Curve::Ecdsa::sign(hash32.data(),
                              keys.privateKey.data(),
                              &this->data.secondSignature);
}

////////////////////////////////////////////////////////////////////////////////
// Verify the Transaction.
auto Transaction::verify() const -> bool {
    // skip both signatures,
    // neither should be present in the signing hash.
    const auto serialized = this->toBytes(true, true);
    const auto hash32 = Hash::sha256(serialized.data(), serialized.size());

    return Curve::Ecdsa::verify(hash32.data(),
                                this->data.senderPublicKey.data(),
                                this->data.signature);
}

////////////////////////////////////////////////////////////////////////////////
// Verify the Transaction using a Second PublicKey.
auto Transaction::secondVerify(const uint8_t *secondPublicKey) const -> bool {
    // include only the first signature,
    // that should be present signing hash for a second signing.
    const auto serialized = this->toBytes(false, true);
    const auto hash32 = Hash::sha256(serialized.data(), serialized.size());

    return Curve::Ecdsa::verify(hash32.data(),
                                secondPublicKey,
                                this->data.secondSignature);
}

////////////////////////////////////////////////////////////////////////////////
// Deserialize the given Hex string via AIP11.
auto Transaction::deserialize(const std::vector<uint8_t> &serialized) -> bool {
    return Deserializer::deserialize(&this->data, serialized);
}

////////////////////////////////////////////////////////////////////////////////
// Serialize the object via AIP11.
auto Transaction::serialize() -> std::vector<uint8_t> {
    return Serializer::serialize(this->data);
}

////////////////////////////////////////////////////////////////////////////////
// Turn the Transaction into its byte representation.
auto Transaction::toBytes(bool excludeSignature,
                          bool excludeSecondSignature) const
                                -> std::vector<uint8_t> {
    return Serializer::serialize(this->data,
                                 { excludeSignature, excludeSecondSignature });
}

////////////////////////////////////////////////////////////////////////////////
// Turn the transaction into a standardized array.
//
// This concept of an array in is quite different compared to other ARK SDKs.
// C++11 doesn't have an 'Any' type, so we'll need to use a string map here.
//
// Json Sizes are approximated using 'https://arduinojson.org/v6/assistant/'
//
// --
auto Transaction::toMap() const -> std::map<std::string, std::string> {
    std::map<std::string, std::string> map;

    size_t jsonSize     = 0UL;

    size_t extraBytes =
        static_cast<size_t>(this->data.vendorField.empty()) +
            this->data.vendorField.size() + sizeof("vendorField") - 1 +
        static_cast<size_t>(this->data.signature.empty()) +
            this->data.signature.size() + sizeof("signature") - 1 +
        static_cast<size_t>(this->data.secondSignature.empty()) +
            this->data.secondSignature.size() + sizeof("secondSignature") - 1;

    // Start with the Transaction's Asset.
    // Tranfer
    if (this->data.type == TRANSFER_TYPE) {
        map = Transfer::getMap(this->data.asset.transfer);
        jsonSize = JSON_OBJECT_SIZE(11) +
                   TRANSACTION_TYPE_TRANSFER_SIZE +
                   extraBytes +
                   289;
    }

    // Second Signature Registration
    if (this->data.type == SECOND_SIGNATURE_TYPE) {
        map = SecondSignature::getMap(this->data.asset.secondSignature);
        jsonSize = 2 * JSON_OBJECT_SIZE(1) +
                       JSON_OBJECT_SIZE(9) +
                       PUBLICKEY_COMPRESSED_LEN +
                       extraBytes +
                       297;
    }

    // Delegate Registration
    if (this->data.type == DELEGATE_REGISTRATION_TYPE) {
        map = DelegateRegistration::getMap(
                this->data.asset.delegateRegistration);
        jsonSize =
                2 * JSON_OBJECT_SIZE(1) +
                JSON_OBJECT_SIZE(9) +
                sizeof("username") - 1 +
                    strtol(map["usernameLen"].c_str(), nullptr, BASE_10) +
                extraBytes +
                251;
    }

    // Vote
    if (this->data.type == VOTE_TYPE) {
        map = Vote::getMap(this->data.asset.vote);
        jsonSize = JSON_ARRAY_SIZE(1) +
                   JSON_OBJECT_SIZE(1) +
                   JSON_OBJECT_SIZE(9) +
                   VOTES_LEN +
                   extraBytes +
                   284;
    }

    // MultiSignature Registration
    // if (this->data.type == MULTI_SIGNATURE_TYPE) {}  // TODO

    // Ipfs
    if (this->data.type == IPFS_TYPE) {
        map = Ipfs::getMap(this->data.asset.ipfs);
        jsonSize = 2 * JSON_OBJECT_SIZE(1) +
                   JSON_OBJECT_SIZE(11) +
                   strtol(map["ipfsLen"].c_str(), nullptr, BASE_10) +
                   sizeof("ipfs") - 1U +
                   extraBytes +
                   218;
    }

    // MultiPayment
    if (this->data.type ==  MULTI_PAYMENT_TYPE) {
        map = MultiPayment::getMap(this->data.asset.multiPayment);
        // Get the number of payments.
        const auto n_payments = strtol(map["n_payments"].c_str(),
                                       nullptr,
                                       BASE_10);
        const auto uint64StrSize = 20;        
        jsonSize =
                JSON_ARRAY_SIZE(n_payments) +
                JSON_OBJECT_SIZE(1) +
                (n_payments * JSON_OBJECT_SIZE(2)) +
                JSON_OBJECT_SIZE(10) +
                (n_payments * (sizeof("amount") - 1 + uint64StrSize +
                    sizeof("recipientId") - 1 + ADDRESS_STR_LEN)) +
                483;
    }

    // Delegate Resignation
    if (this->data.type == DELEGATE_RESIGNATION_TYPE) {
        jsonSize = JSON_OBJECT_SIZE(10) + extraBytes * 205;
    }

    // Htlc Lock
    if (this->data.type == HTLC_LOCK_TYPE) {
        map = HtlcLock::getMap(this->data.asset.htlcLock);
        jsonSize = JSON_OBJECT_SIZE(1) +
                   (2 * JSON_OBJECT_SIZE(2)) +
                   JSON_OBJECT_SIZE(11) +
                   HTLC_LOCK_SIZE +
                   extraBytes +
                   368;
    }

    // Htlc Claim
    if (this->data.type == HTLC_CLAIM_TYPE) {
        map = HtlcClaim::getMap(this->data.asset.htlcClaim);
        jsonSize = JSON_OBJECT_SIZE(1) +
                   JSON_OBJECT_SIZE(2) +
                   JSON_OBJECT_SIZE(9) +
                   HASH_32_LEN + HASH_32_LEN +
                   extraBytes +
                   337;
    }

    // Htlc Refund
    if (this->data.type == HTLC_REFUND_TYPE) {
        map = HtlcRefund::getMap(this->data.asset.htlcRefund);
        jsonSize = 2 * JSON_OBJECT_SIZE(1) +
                       JSON_OBJECT_SIZE(11) +
                       HASH_32_LEN +
                       extraBytes +
                       445;
    }

    // Scooter Registration
    if (this->data.typeGroup == SCOOTER_TYPEGROUP && this->data.type == SCOOTER_TYPE) {
        map = ScooterRegistration::getMap(this->data.asset.scooterRegistration);
        jsonSize = 2 * JSON_OBJECT_SIZE(1) +
                       JSON_OBJECT_SIZE(9) +
                       sizeof("scooterId") - 1 + SCOOTER_ID_LEN +
                       extraBytes +
                       233;
    }

    // Continue with the Common variables
    //  Version
    map.emplace("version", UintToString(this->data.version));

    //  Network
    map.emplace("network", UintToString(this->data.network));

    // v2
    if (this->data.version == TRANSACTION_VERSION_TYPE_2) {
        // TypeGroup
        map.emplace("typeGroup", UintToString(this->data.typeGroup));
    }

    //  Type
    map.emplace("type", UintToString(this->data.type));

    // v2
    if (this->data.version == TRANSACTION_VERSION_TYPE_2) {
        // Nonce
        map.emplace("nonce", UintToString(this->data.nonce));
    }
    // v1
    else if (this->data.version == TRANSACTION_VERSION_TYPE_1) {
        // Timestamp
        map.emplace("timestamp", UintToString(this->data.timestamp));
    }

    // Sender PublicKey
    map.emplace("senderPublicKey", BytesToHex(this->data.senderPublicKey));

    //  Fee
    map.emplace("fee", UintToString(this->data.fee));

    // VendorField
    if (!this->data.vendorField.empty()) {
        const auto vf = reinterpret_cast<const char *>(
                this->data.vendorField.data());

        map.emplace("vendorField",
                    std::string(vf, vf + this->data.vendorField.size()));
    }

    // Signature
    if (!this->data.signature.empty()) {
        map.emplace("signature", BytesToHex(this->data.signature));
    }

    // Second Signature
    if (!this->data.secondSignature.empty()) {
        map.emplace("secondSignature", BytesToHex(this->data.secondSignature));
    }

    // Transaction Id
    map.emplace("id", BytesToHex(this->getId()));

    // v2
    if (this->data.version == TRANSACTION_VERSION_TYPE_2) {
        jsonSize += VF_OFFSET;
    }
    // v1
    else if (this->data.version == TRANSACTION_VERSION_TYPE_1) {
        jsonSize += v1::VF_OFFSET;
    }

    map.emplace("jsonSize", UintToString(jsonSize));

    return map;
}

////////////////////////////////////////////////////////////////////////////////
// Turn the Transaction into a JSON string using `toMap` as the source.
auto Transaction::toJson() const -> std::string {
    auto txArray = this->toMap();

    const auto jsonSize = strtol(txArray["jsonSize"].c_str(), nullptr, BASE_10);
    const auto docCapacity = jsonSize;
    DynamicJsonDocument doc(docCapacity);

    // Version
    doc["version"] = strtol(txArray["version"].c_str(), nullptr, BASE_10);

    // Network
    doc["network"] = strtol(txArray["network"].c_str(), nullptr, BASE_10);

    // v2
    if (this->data.version == TRANSACTION_VERSION_TYPE_2) {
        // TypeGroup
        doc["typeGroup"] = strtol(txArray["typeGroup"].c_str(),
                                  nullptr,
                                  BASE_10);
    }

    // Type
    doc["type"] = strtol(txArray["type"].c_str(), nullptr, BASE_10);

    // v2
    if (this->data.version == TRANSACTION_VERSION_TYPE_2) {
        // Nonce
        doc["nonce"] = txArray["nonce"];
    }
    // v1
    else if (this->data.version == TRANSACTION_VERSION_TYPE_1) {
        // Timestamp
        doc["timestamp"] = txArray["timestamp"];
    }

    // Sender PublicKey
    doc["senderPublicKey"] = txArray["senderPublicKey"];

    // Fee
    doc["fee"] = txArray["fee"];

    // VendorField
    if (!this->data.vendorField.empty()) {
        doc["vendorField"] = txArray["vendorField"];
    }

    // Assets
    // Transfer
    if (this->data.type == TRANSFER_TYPE) {
        // Amount
        doc["amount"] = txArray["amount"];

        // Expiration
        doc["expiration"] = strtol(txArray["expiration"].c_str(),
                                   nullptr,
                                   BASE_10);
        // RecipientId
        doc["recipientId"] = txArray["recipientId"];
    }

    // Second Signature Registration
    if (this->data.type == SECOND_SIGNATURE_TYPE) {
        JsonObject asset = doc.createNestedObject("asset");
        JsonObject secondSignature = asset.createNestedObject("signature");

        // Second PublicKey
        secondSignature["publicKey"] = txArray["publicKey"];
    }

    // Delegate Registration
    if (this->data.type == DELEGATE_REGISTRATION_TYPE) {
        JsonObject asset = doc.createNestedObject("asset");
        JsonObject registration = asset.createNestedObject("delegate");

        // Username
        registration["username"] = txArray["username"];
    }

    // Vote
    if (this->data.type == VOTE_TYPE) {
        JsonObject asset = doc.createNestedObject("asset");
        JsonArray votes = asset.createNestedArray("votes");

        // Votes
        votes.add(txArray["votes"]);
    }

    // MultiSignature Registration
    // if (this->data.type == MULTI_SIGNATURE_TYPE) {}  // TODO

    // Ipfs
    if (this->data.type == IPFS_TYPE) {
        JsonObject asset = doc.createNestedObject("asset");

        // Ipfs DAG
        asset["ipfs"] = txArray["ipfs"];
    }

    // MultiPayment
    if (this->data.type == MULTI_PAYMENT_TYPE) {
        JsonObject asset = doc.createNestedObject("asset");
        JsonArray payments = asset.createNestedArray("payments");

        const auto paymentsStr = txArray["amounts"];
        const auto addressesStr = txArray["addresses"];
        const auto n_payments = strtol(txArray["n_payments"].c_str(),
                                       nullptr,
                                       BASE_10);

        for (uint8_t i = 0U; i < n_payments; ++i) {
            JsonObject payment_n = payments.createNestedObject();

            // Ammount(N)
            payment_n["amount"] = paymentsStr
                    .substr(0, paymentsStr.find(',', 0));

            // RecipientId(N)
            payment_n["recipientId"] = addressesStr
                .substr(i + (i * ADDRESS_STR_LEN), ADDRESS_STR_LEN);
        }
    }

    // Delegate Resignation
    // No Asset Needed.

    // HTLC Lock
    if (this->data.type == HTLC_LOCK_TYPE) {
        // Amount
        doc["amount"] = txArray["amount"];

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

        JsonObject asset = doc.createNestedObject("asset");
        JsonObject lock = asset.createNestedObject("lock");

        // Secret Hash
        lock["secretHash"] = txArray["secretHash"];

        JsonObject expiration = lock.createNestedObject("expiration");

        // Expiration Type
        expiration["type"] = strtol(txArray["expirationType"].c_str(),
                                    nullptr,
                                    BASE_10);
        // Expiration Value
        expiration["value"] = strtol(txArray["expiration"].c_str(),
                                     nullptr,
                                     BASE_10);
    }

    // HTLC Claim
    if (this->data.type == HTLC_CLAIM_TYPE) {
        JsonObject asset = doc.createNestedObject("asset");
        JsonObject claim = asset.createNestedObject("claim");

        // Lock Transaction Id
        claim["lockTransactionId"] = txArray["lockTransactionId"];

        // Unlock Secret
        claim["unlockSecret"] = txArray["unlockSecret"];
    }

    // HTLC Refund
    if (this->data.type == HTLC_REFUND_TYPE) {
        JsonObject asset = doc.createNestedObject("asset");
        JsonObject refund = asset.createNestedObject("refund");

        // Lock Transaction Id
        refund["lockTransactionId"] = txArray["lockTransactionId"];
    }



    // Scooter Registration
    if (this->data.typeGroup == SCOOTER_TYPEGROUP && this->data.type == SCOOTER_TYPE) {
        doc["amount"] = "0";

        JsonObject asset = doc.createNestedObject("asset");

        // ScooterId
        asset["scooterId"] = txArray["scooterId"];
    }


    // Signature
    if (!this->data.signature.empty()) {
        doc["signature"] = txArray["signature"];
    }

    // Second Signature
    if (!this->data.secondSignature.empty()) {
        doc["secondSignature"] = txArray["secondSignature"];
    }

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

    std::string jsonStr;
    jsonStr.reserve(docCapacity);
    serializeJson(doc, jsonStr);

    return jsonStr;
}

}  // namespace transactions
}  // namespace Crypto
}  // namespace Ark
/**
 * 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.
 **/

#ifndef ARK_TRANSACTIONS_TYPES_ASSETS_HPP
#define ARK_TRANSACTIONS_TYPES_ASSETS_HPP

#include "transactions/defaults/types.hpp"

#include "transactions/types/transfer.hpp"
#include "transactions/types/second_signature.hpp"
#include "transactions/types/delegate_registration.hpp"
#include "transactions/types/vote.hpp"
// #include "transactions/types/multi_signature.hpp"
#include "transactions/types/ipfs.hpp"
#include "transactions/types/multi_payment.hpp"
#include "transactions/types/delegate_resignation.hpp"
#include "transactions/types/htlc_lock.hpp"
#include "transactions/types/htlc_claim.hpp"
#include "transactions/types/htlc_refund.hpp"

#include "transactions/types/scooter_registration.hpp"

namespace Ark {
namespace Crypto {
namespace transactions {

////////////////////////////////////////////////////////////////////////////////
// AIP-11 Transaction Assets
typedef struct tx_asset_t {
    Transfer                    transfer;               // Type 0
    SecondSignature             secondSignature;        // Type 1
    DelegateRegistration        delegateRegistration;   // Type 2
    Vote                        vote;                   // Type 3
    // MultiSignature              multiSignature;         // Type 4
    Ipfs                        ipfs;                   // Type 5
    MultiPayment                multiPayment;           // Type 6
    DelegateResignation         delegateResignation;    // Type 7
    HtlcLock                    htlcLock;               // Type 8
    HtlcClaim                   htlcClaim;              // Type 9
    HtlcRefund                  htlcRefund;             // Type 10
    ScooterRegistration         scooterRegistration;    // TypeGroup 4000, type 400
} Asset;

}  // namespace transactions
}  // namespace Crypto
}  // namespace Ark

#endif
/**
 * 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 "transactions/types/scooter_registration.hpp"

#include <cstdint>
#include <map>
#include <string>
#include <utility>

namespace Ark {
namespace Crypto {
namespace transactions {

////////////////////////////////////////////////////////////////////////////////
// Deserialize Scooter Registration (TypeGroup 4000, Type 400) - 11 bytes
//
// @param ScooterRegistration *registration
// @param uint8_t *buffer: The serialized buffer beginning at the Assets offset.
//
// @return Asset Length
//
// ---
// Internals:
//
// ScooterId Len - 1 Byte
// - registration->scooterId.resize(buffer[0]);
//
// ScooterId - 10 Bytes:
// - std::move(&buffer[1], &buffer[1 + 10], registration->scooterId.data());
//
// ---
auto ScooterRegistration::Deserialize(ScooterRegistration *registration,
                                      const uint8_t *buffer)
        -> size_t {
    registration->scooterId.resize(buffer[0]);                      // 1 Byte

    std::move(&buffer[sizeof(uint8_t)],                             // 10 Bytes
              &buffer[sizeof(uint8_t) + SCOOTER_ID_LEN],
              registration->scooterId.begin());

    return sizeof(uint8_t) + SCOOTER_ID_LEN;                        // 11 Bytes
}

////////////////////////////////////////////////////////////////////////////////
// Serialize Scooter Registration (TypeGroup 4000, Type 400) - 11 bytes
//
// @param const ScooterRegistration &registration
// @param uint8_t *buffer: The serialized buffer at the Assets offset.
//
// @return Asset Length
//
// ---
// Internals:
//
// ScooterId Length - 1 Byte
// - buffer[0] = static_cast<uint8_t>(10);
//
// ScooterId - 10 Bytes:
// - std::move(registration.scooterId.begin(), registration.scooterId.end(), &buffer[1]);
//
// ---
auto ScooterRegistration::Serialize(const ScooterRegistration &registration,
                                    uint8_t *buffer) -> size_t {
    buffer[0] =                                                     // 1 Byte
            static_cast<uint8_t>(registration.scooterId.length());

    std::move(registration.scooterId.begin(),                       // 10 Bytes
              registration.scooterId.end(),
              &buffer[sizeof(uint8_t)]);

    return sizeof(uint8_t) + SCOOTER_ID_LEN;                        // 11 Bytes
}

////////////////////////////////////////////////////////////////////////////////
// Return a Map of the Transfer asset.
auto ScooterRegistration::getMap(const ScooterRegistration &registration)
        -> std::map<std::string, std::string> {
    std::map<std::string, std::string> map;

    // ScooterId
    map.emplace("scooterId", registration.scooterId);

    return map;
}

}  // namespace transactions
}  // namespace Crypto
}  // namespace Ark
/**
 * 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.
 **/

#ifndef ARK_TRANSACTIONS_TYPES_SCOOTER_REGISTRATION_HPP
#define ARK_TRANSACTIONS_TYPES_SCOOTER_REGISTRATION_HPP

#include <array>
#include <cstdint>
#include <cstring>
#include <map>
#include <string>

#include "interfaces/constants.h"

#include "utils/json.h"

namespace Ark {
namespace Crypto {
namespace transactions {

const uint32_t SCOOTER_TYPEGROUP    = 4000UL;
const uint16_t SCOOTER_TYPE         = 400UL;

const size_t SCOOTER_ID_LEN         = 10UL;

////////////////////////////////////////////////////////////////////////////////
// TypeGroup 4000, Type 400 - Scooter Registration
struct ScooterRegistration {
    ////////////////////////////////////////////////////////////////////////////
    std::string scooterId;

    ////////////////////////////////////////////////////////////////////////////
    static size_t Deserialize(ScooterRegistration *registration,
                              const uint8_t *buffer);

    ////////////////////////////////////////////////////////////////////////////
    static size_t Serialize(const ScooterRegistration &registration,
                            uint8_t *buffer);

    ////////////////////////////////////////////////////////////////////////////
    static std::map<std::string, std::string> getMap(
            const ScooterRegistration &registration);

    ////////////////////////////////////////////////////////////////////////////
    ScooterRegistration() : scooterId() {}
};

}  // namespace transactions
}  // namespace Crypto
}  // namespace Ark

#endif

cmake_minimum_required(VERSION 3.2)

project(${PROJECT_NAME}_tests C CXX)

set (PROJECT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../lib")

# ------------------------------------------------------------------------------
# External Libraries
# ------------------------------------------------------------------------------

include(${CMAKE_SOURCE_DIR}/cmake/GTest.cmake)

include_directories(${ARDUINO_JSON_SOURCE_DIR})

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Link the directories to be included
# ------------------------------------------------------------------------------

include_directories(${PROJECT_SOURCE_DIR})
include_directories(${PROJECT_SOURCE_DIR}/../src)
include_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY})

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# ARK C++ Crypto Test Source
# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Common

set(COMMON_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/common/configuration.cpp
    ${PROJECT_SOURCE_DIR}/common/fee_policy.cpp
    ${PROJECT_SOURCE_DIR}/common/network.cpp
)

# ------------------------------------------------------------------------------
# Crypto

set(CRYPTO_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/crypto/curve_ecdsa_sign_null_hash.cpp
    ${PROJECT_SOURCE_DIR}/crypto/curve_ecdsa_sign_null_privatekey.cpp
    ${PROJECT_SOURCE_DIR}/crypto/curve_ecdsa_sign.cpp
    ${PROJECT_SOURCE_DIR}/crypto/curve_publickey.cpp
    ${PROJECT_SOURCE_DIR}/crypto/curve_verify_invalid.cpp
    ${PROJECT_SOURCE_DIR}/crypto/curve_verify_valid.cpp
    ${PROJECT_SOURCE_DIR}/crypto/hash.cpp
    ${PROJECT_SOURCE_DIR}/crypto/message_sign.cpp
    ${PROJECT_SOURCE_DIR}/crypto/message_verify.cpp
    ${PROJECT_SOURCE_DIR}/crypto/message.cpp
    ${PROJECT_SOURCE_DIR}/crypto/slot.cpp
)

# ------------------------------------------------------------------------------
# Identities

set(IDENTITIES_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/identities/address.cpp
    ${PROJECT_SOURCE_DIR}/identities/keys.cpp
    ${PROJECT_SOURCE_DIR}/identities/keys_publickey.cpp
    ${PROJECT_SOURCE_DIR}/identities/privatekey.cpp
    ${PROJECT_SOURCE_DIR}/identities/publickey.cpp
    ${PROJECT_SOURCE_DIR}/identities/wif.cpp
)

# ------------------------------------------------------------------------------
# Managers

set(MANAGERS_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/managers/fee_manager.cpp
    ${PROJECT_SOURCE_DIR}/managers/network_manager.cpp
)

# ------------------------------------------------------------------------------
# Networks

set(NETWORKS_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/networks/devnet.cpp
    ${PROJECT_SOURCE_DIR}/networks/mainnet.cpp
    ${PROJECT_SOURCE_DIR}/networks/testnet.cpp
)

# ------------------------------------------------------------------------------
# Transactions Test Sources
# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Builders

set(TRANSACTIONS_BUILDERS_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/transactions/builders/transfer.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/second_signature.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/delegate_registration.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/vote.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/ipfs.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/multi_payment.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/htlc_lock.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/htlc_claim.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/htlc_refund.cpp
    ${PROJECT_SOURCE_DIR}/transactions/builders/scooter_registration.cpp
)

# ------------------------------------------------------------------------------
# Defaults

set(TRANSACTIONS_DEFAULTS_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/transactions/defaults/fees_types.cpp
)


# ------------------------------------------------------------------------------
# Serialization/Deserialization

set(TRANSACTIONS_SERDE_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/transactions/deserializer.cpp
    ${PROJECT_SOURCE_DIR}/transactions/serializer.cpp
)

# ------------------------------------------------------------------------------
# v1 Types

set(TRANSACTIONS_TYPES_V1_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/transactions/types/transfer_v1.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/second_signature_v1.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/delegate_registration_v1.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/vote_v1.cpp
)

# ------------------------------------------------------------------------------
# Types

set(TRANSACTIONS_TYPES_TEST_SOURCE
    ${TRANSACTIONS_TYPES_V1_SOURCE}
    ${PROJECT_SOURCE_DIR}/transactions/types/transfer.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/second_signature.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/delegate_registration.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/vote.cpp
    # ${PROJECT_SOURCE_DIR}/transactions/types/multi_signature.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/ipfs.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/multi_payment.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/delegate_resignation.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/htlc_lock.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/htlc_claim.cpp
    ${PROJECT_SOURCE_DIR}/transactions/types/htlc_refund.cpp
)

# ------------------------------------------------------------------------------
# Transactions Test Source Files

set(TRANSACTIONS_TEST_SOURCE
    ${TRANSACTIONS_BUILDERS_TEST_SOURCE}
    ${TRANSACTIONS_DEFAULTS_TEST_SOURCE}
    ${TRANSACTIONS_SERDE_TEST_SOURCE}
    ${TRANSACTIONS_TYPES_V1_TEST_SOURCE}
    ${TRANSACTIONS_TYPES_TEST_SOURCE}
    ${TRANSACTIONS_TRANSACTION_TEST_SOURCE}
    ${PROJECT_SOURCE_DIR}/transactions/transaction.cpp
    ${PROJECT_SOURCE_DIR}/transactions/transaction_v1.cpp
)

# ------------------------------------------------------------------------------
# Utils

set(UTILS_TEST_SOURCE
    ${PROJECT_SOURCE_DIR}/utils/base58.cpp
    ${PROJECT_SOURCE_DIR}/utils/crypto_helpers.cpp
    ${PROJECT_SOURCE_DIR}/utils/hex.cpp
    ${PROJECT_SOURCE_DIR}/utils/str.cpp
    ${PROJECT_SOURCE_DIR}/utils/unpack.cpp
)

# ------------------------------------------------------------------------------
# ARK C++ Library Test Source

set(ARK_TEST_SOURCE
    ${COMMON_TEST_SOURCE}
    ${CRYPTO_TEST_SOURCE}
    ${IDENTITIES_TEST_SOURCE}
    ${MANAGERS_TEST_SOURCE}
    ${NETWORKS_TEST_SOURCE}
    ${TRANSACTIONS_TEST_SOURCE}
    ${UTILS_TEST_SOURCE}
)

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Link ARK C++ Crypto to the Test Libraries
# ------------------------------------------------------------------------------

find_library(${PROJECT_NAME} PUBLIC)

add_executable(${PROJECT_NAME} ${ARK_TEST_SOURCE})

target_link_libraries(${PROJECT_NAME} ark_cpp_crypto gtest gtest_main)

add_test(NAME test COMMAND ${PROJECT_NAME})

# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# Coverage
# ------------------------------------------------------------------------------

if (CMAKE_BUILD_TYPE STREQUAL "Coverage")
    include("${CMAKE_SOURCE_DIR}/cmake/CodeCoverage.cmake")

    setup_target_for_coverage(${PROJECT_NAME}_coverage ${PROJECT_NAME}_tests coverage)

    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
endif()  #CMAKE_BUILD_TYPE STREQUAL "Coverage"

# ------------------------------------------------------------------------------
/**
 * 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 "gtest/gtest.h"

#include "transactions/builders/scooter_registration.hpp"

#include "common/configuration.hpp"

#include "identities/keys.hpp"

#include "test_helpers.h"

using namespace Ark::Crypto;
using namespace Ark::Crypto::transactions;

////////////////////////////////////////////////////////////////////////////////
TEST(transactions_builder, scooter_registration) {  // NOLINT
    const Network Radians = {
        "f39a61f04d6136a690a0b675ef6eedbd053665bd343b4e4f03311f12065fb875",
        1, 0xCE, 0x41,
        "2019-10-25T09:05:40.856Z"
    };

    const Configuration radiansCfg(Radians);

    const uint8_t radiansRecipient[] = {
        65, 29,  252, 105, 181, 76,  127, 233, 1,  233, 29,
        90, 154, 183, 131, 136, 100, 94,  36,  39, 234 };

    const auto passphrase   = "vehicle smile daughter torch often ask rare crucial best cargo half lady";
    const auto publicKey    = identities::Keys::fromPassphrase(passphrase).publicKey;

    auto transaction = builder::ScooterRegistration()
        .typeGroup(SCOOTER_TYPEGROUP)
        .type(SCOOTER_TYPE)
        .nonce(3)
        .senderPublicKey(publicKey.data())
        .fee(3000000000ULL)
        .scooterId("1234567890")
        .build(radiansCfg);

    transaction.sign(passphrase);

    ASSERT_TRUE(transaction.verify());
}