diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a145976..b6a3bb2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ SET(FILES ${CMAKE_SOURCE_DIR}/asset_utils.cpp ${CMAKE_SOURCE_DIR}/sc_utils.cpp ${CMAKE_SOURCE_DIR}/test_utils.cpp ${CMAKE_SOURCE_DIR}/wallet_utils.cpp + ${CMAKE_SOURCE_DIR}/utils.cpp ) SET(HEADER_FILES argparser.h diff --git a/README.md b/README.md index f7992db3..1ab6568e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,8 @@ Basic config: Do action although an error has been detected. Currently only implemented for proposals. -enabletestcontracts Enable test contract indices and names for commands using a contract index parameter. This flag has to be passed before the contract index/name. The node to connect to needs to have test contracts running. - + -print-only + Print the raw transaction data without sending it to the network. Useful for offline signing or broadcasting later. Commands: [WALLET COMMANDS] diff --git a/argparser.h b/argparser.h index 434dda88..f185b09c 100644 --- a/argparser.h +++ b/argparser.h @@ -43,6 +43,8 @@ void print_help() printf("\t\tDo action although an error has been detected. Currently only implemented for proposals.\n"); printf("\t-enabletestcontracts\n"); printf("\t\tEnable test contract indices and names for commands using a contract index parameter. This flag has to be passed before the contract index/name. The node to connect to needs to have test contracts running.\n"); + printf("\t-print-only \n"); + printf("\t\tPrint the raw transaction data without sending it to the network. Useful for offline signing or broadcasting later.\n"); printf("\nCommands:\n"); printf("\n[WALLET COMMANDS]\n"); @@ -696,6 +698,13 @@ void parseArgument(int argc, char** argv) ++i; continue; } + if (strcmp(argv[i], "-print-only") == 0) + { + CHECK_NUMBER_OF_PARAMETERS(1); + g_printToScreen = argv[i+1]; + i+=2; + continue; + } /*************************** ***** WALLET COMMANDS ***** diff --git a/connection.cpp b/connection.cpp index 0ba99ae9..5a1a074e 100644 --- a/connection.cpp +++ b/connection.cpp @@ -285,18 +285,30 @@ std::vector QubicConnection::getLatestVectorPacketAs() int QubicConnection::sendData(uint8_t* buffer, int sz) { - int size = sz; - int numberOfBytes; - while (size) - { - if ((numberOfBytes = send(mSocket, (char*)buffer, size, 0)) <= 0) + // also skip printing packets of size 8 (typically used during the preparation step, not the final stage) + if (!std::string(g_printToScreen).empty() && sz != 8) { + std::string printType = g_printToScreen; + // Do not print the first 8 bytes (header) + printBytes(buffer + 8, sz - 8, printType); + + // this operation may break the normal flow, we need to skip printing error messages to console + if (!std::freopen("/dev/null", "w", stdout)) {} + if (!std::freopen("/dev/null", "w", stderr)) {} + return 0; + } else { + int size = sz; + int numberOfBytes; + while (size) { - return 0; + if ((numberOfBytes = send(mSocket, (char*)buffer, size, 0)) <= 0) + { + return 0; + } + buffer += numberOfBytes; + size -= numberOfBytes; } - buffer += numberOfBytes; - size -= numberOfBytes; + return sz - size; } - return sz - size; } template SpecialCommand QubicConnection::receivePacketWithHeaderAs(); diff --git a/utils.cpp b/utils.cpp new file mode 100644 index 00000000..25bc2f3e --- /dev/null +++ b/utils.cpp @@ -0,0 +1,3 @@ +#include "utils.h" + +char* g_printToScreen = (char *)""; \ No newline at end of file diff --git a/utils.h b/utils.h index 2240b162..6855fd93 100644 --- a/utils.h +++ b/utils.h @@ -4,6 +4,11 @@ #include #include +extern char* g_printToScreen; + +static const char B64_TABLE[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + static void byteToHex(const uint8_t* byte, char* hex, const int sizeInByte) { for (int i = 0; i < sizeInByte; i++) @@ -96,3 +101,70 @@ static std::string unwrapString(const std::string& str, char wrapperCharFront, c } return str; } + +static inline std::string base64_encode(const std::vector &in) { + std::string out; + int val = 0, valb = -6; + + for (uint8_t c : in) { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) { + out.push_back(B64_TABLE[(val >> valb) & 0x3F]); + valb -= 6; + } + } + if (valb > -6) + out.push_back(B64_TABLE[((val << 8) >> (valb + 8)) & 0x3F]); + + while (out.size() % 4) + out.push_back('='); + + return out; +} + +static std::string base64_encode(uint8_t *data, size_t length) { + return base64_encode(std::vector(data, data + length)); +} + +static std::vector base64_decode(const std::string &in) { + static int T[256]; + static bool init = false; + + if (!init) { + for (int i = 0; i < 256; i++) T[i] = -1; + for (int i = 0; i < 64; i++) T[(unsigned char)B64_TABLE[i]] = i; + init = true; + } + + std::vector out; + int val = 0, valb = -8; + + for (unsigned char c : in) { + if (T[c] == -1) continue; + val = (val << 6) + T[c]; + valb += 6; + + if (valb >= 0) { + out.push_back(uint8_t((val >> valb) & 0xFF)); + valb -= 8; + } + } + + return out; +} + +static void printBytes(uint8_t* data, size_t length, std::string type = "base64") {\ + printf("---------------- %s ----------------\n", type.c_str()); + if (type == "base64") { + std::string encoded = base64_encode(data, length); + printf("%s\n", encoded.c_str()); + } else if (type == "hex") { + for (size_t i = 0; i < length; i++) { + printf("%02x", data[i]); + } + printf("\n"); + } else { + printf("Unsupported print type: %s\n", type.c_str()); + } +} \ No newline at end of file diff --git a/wallet_utils.cpp b/wallet_utils.cpp index 4c78c604..edcc6647 100644 --- a/wallet_utils.cpp +++ b/wallet_utils.cpp @@ -265,6 +265,9 @@ void makeCustomTransaction(const char* nodeIp, int nodePort, const uint8_t* extraData, uint32_t scheduledTickOffset) { + if (extraDataSize < 0) + throw std::invalid_argument("extraDataSize < 0"); + auto qc = make_qc(nodeIp, nodePort); uint8_t privateKey[32] = {0}; uint8_t sourcePublicKey[32] = {0};