diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b2102f4b..99d0cfbc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ option(WITH_ICU "Compile with International Components for Unicode." OFF) option(WITH_PNG "Compile with Libpng support." OFF) option(WITH_QRENCODE "Compile with QREncode." OFF) option(JUST_KTH_SOURCES "Just Knuth source code to be linted." OFF) +option(WITH_CONSOLE "Compile console application." OFF) option(GLOBAL_BUILD "" OFF) @@ -461,6 +462,55 @@ endif() _group_sources(${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}") +# Console +# ------------------------------------------------------------------------------ +message(STATUS "Knuth: WITH_CONSOLE ${WITH_CONSOLE}") +if (WITH_CONSOLE) + set(_test_console_sources + console/main.cpp + ) + + add_executable(kth_domain_console ${_test_console_sources}) + + target_link_libraries(kth_domain_console ${PROJECT_NAME}) + + set_target_properties( + kth_domain_console PROPERTIES + FOLDER "domain" + OUTPUT_NAME kth_domain_console) + + + # Enable sanitizers and other debug options only in debug mode + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + message(STATUS "Activating sanitizers and debug options") + message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}") + message(STATUS "APPLE: ${APPLE}") + + if (APPLE) + # In macOS, use only AddressSanitizer and UndefinedBehaviorSanitizer + target_compile_options(kth_domain_console PRIVATE + -fsanitize=address,undefined + -fno-omit-frame-pointer + -g + ) + target_link_options(kth_domain_console PRIVATE + -fsanitize=address,undefined + ) + else() + # In other operating systems (like Linux), add LeakSanitizer also + target_compile_options(kth_domain_console PRIVATE + -fsanitize=address,undefined,leak + -fno-omit-frame-pointer + -g + ) + target_link_options(kth_domain_console PRIVATE + -fsanitize=address,undefined,leak + ) + endif() + + endif() +endif() + # Tests # ------------------------------------------------------------------------------ if (WITH_TESTS) diff --git a/console/main.cpp b/console/main.cpp new file mode 100644 index 000000000..50b634e5d --- /dev/null +++ b/console/main.cpp @@ -0,0 +1,16 @@ +#include +#include + +int main() { + using kth::domain::wallet::payment_address; + + std::cout << "start" << std::endl; + payment_address const address("bitcoincash:pvstqkm54dtvnpyqxt5m5n7sjsn4enrlxc526xyxlnjkaycdzfeu69reyzmqx"); + std::cout << "address created" << std::endl; + std::cout << "address is valid: " << bool(address) << std::endl; + std::cout << "address encoded cashaddr: " << address.encoded_cashaddr(false) << std::endl; + std::cout << "address encoded token address: " << address.encoded_cashaddr(true) << std::endl; + std::cout << "address encoded legacy: " << address.encoded_legacy() << std::endl; + + return 0; +} diff --git a/include/kth/domain/wallet/payment_address.hpp b/include/kth/domain/wallet/payment_address.hpp index 2aaad0ec0..30ac9c888 100644 --- a/include/kth/domain/wallet/payment_address.hpp +++ b/include/kth/domain/wallet/payment_address.hpp @@ -53,17 +53,22 @@ class KD_API payment_address { /// Constructors. payment_address() = default; - payment_address(payment_address const& x) = default; + // payment_address(payment_address const& x) = default; + // payment_address& operator=(payment_address const& x) = default; // payment_address(payment_address&& x) noexcept; + // payment_address(payment_address&& x) noexcept = default; + // payment_address(payment_address&&) = delete; + // payment_address& operator=(payment_address&&) = delete; + payment_address(payment const& decoded); payment_address(ec_private const& secret); payment_address(std::string const& address); payment_address(short_hash const& hash, uint8_t version = mainnet_p2kh); + payment_address(hash_digest const& hash, uint8_t version = mainnet_p2kh); payment_address(ec_public const& point, uint8_t version = mainnet_p2kh); payment_address(chain::script const& script, uint8_t version = mainnet_p2sh); - payment_address& operator=(payment_address const& x) = default; /// Operators. bool operator==(payment_address const& x) const; @@ -93,9 +98,8 @@ class KD_API payment_address { [[nodiscard]] uint8_t version() const; - //TODO(fernando): re-enable this - // [[nodiscard]] - // byte_span hash() const; + [[nodiscard]] + byte_span hash() const; [[nodiscard]] short_hash hash20() const; @@ -157,6 +161,12 @@ struct KD_API wrapped_data { uint32_t checksum; }; + +// static_assert(std::is_copy_constructible_v, "payment_address should be copy constructible."); +// static_assert(std::is_copy_assignable_v, "payment_address should be copy assignable."); +// static_assert( ! std::is_move_constructible_v, "payment_address should not be move constructible."); +// static_assert( ! std::is_move_assignable_v, "payment_address should not be move assignable."); + } // namespace kth::domain::wallet // Allow payment_address to be in indexed in std::*map classes. @@ -165,8 +175,9 @@ template <> struct hash { size_t operator()(kth::domain::wallet::payment_address const& address) const { //TODO(fernando): re-enable this - // return std::hash()(address.hash()); - return std::hash()(address.hash20()); + // return std::hash()(address.hash32()); + // return std::hash()(address.hash20()); + return std::hash()(address.hash32()); } }; } // namespace std diff --git a/src/wallet/payment_address.cpp b/src/wallet/payment_address.cpp index 1b0eb5529..7600162a1 100644 --- a/src/wallet/payment_address.cpp +++ b/src/wallet/payment_address.cpp @@ -33,29 +33,71 @@ using namespace kth::infrastructure::wallet; namespace kth::domain::wallet { payment_address::payment_address(payment const& decoded) - : payment_address(from_payment(decoded)) -{} + : payment_address(payment_address{from_payment(decoded)}) +{ + std::cout << "payment_address(payment const& decoded)" << std::endl; +} payment_address::payment_address(std::string const& address) - : payment_address(from_string(address)) -{} + : payment_address(payment_address{from_string(address)}) +{ + std::cout << "payment_address(std::string const& address)" << std::endl; + + printf("hash_data_.data(): %p\n", hash_data_.data()); + + std::cout << "hash().size(): " << this->hash().size() << std::endl; + std::cout << "hash_span_.size(): " << hash_span_.size() << std::endl; + + for (auto byte : this->hash()) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte); + } + std::cout << std::endl; + std::cout << "----------------------------------------" << std::endl; +} payment_address::payment_address(ec_private const& secret) - : payment_address(from_private(secret)) -{} + : payment_address(payment_address{from_private(secret)}) +{ + std::cout << "payment_address(ec_private const& secret)" << std::endl; +} payment_address::payment_address(ec_public const& point, uint8_t version) - : payment_address(from_public(point, version)) -{} + : payment_address(payment_address{from_public(point, version)}) +{ + std::cout << "payment_address(ec_public const& point, uint8_t version)" << std::endl; +} payment_address::payment_address(chain::script const& script, uint8_t version) - : payment_address(from_script(script, version)) -{} + : payment_address(payment_address{from_script(script, version)}) +{ + std::cout << "payment_address(chain::script const& script, uint8_t version)" << std::endl; +} payment_address::payment_address(short_hash const& hash, uint8_t version) + : valid_(true) + , version_(version) + , hash_span_{hash_data_.begin(), hash.size()} +{ + std::cout << "payment_address(short_hash const& hash, uint8_t version)" << std::endl; + std::copy_n(hash.begin(), hash.size(), hash_data_.begin()); +} + +payment_address::payment_address(hash_digest const& hash, uint8_t version) : valid_(true), version_(version), hash_span_{hash_data_.begin(), hash.size()} { + std::cout << "payment_address(hash_digest const& hash, uint8_t version)" << std::endl; + printf("hash_data_.data(): %p\n", hash_data_.data()); std::copy_n(hash.begin(), hash.size(), hash_data_.begin()); + printf("hash_data_.data(): %p\n", hash_data_.data()); + + std::cout << "hash().size(): " << this->hash().size() << std::endl; + std::cout << "hash_span_.size(): " << hash_span_.size() << std::endl; + // print the hash in hex format (each byte as 2 chars) + for (auto byte : this->hash()) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte); + } + std::cout << std::endl; + std::cout << "----------------------------------------" << std::endl; } // Validators. @@ -160,13 +202,36 @@ payment_address payment_address::from_string_cashaddr(std::string const& address return {}; } - short_hash hash; - std::copy(std::begin(data) + 1, std::end(data), std::begin(hash)); + if (data.size() == short_hash_size + 1) { + std::cout << "***** 20 bytes hash" << std::endl; + short_hash hash; + if ((data.size() - 1) != hash.size()) { + return {}; + } + std::copy(std::begin(data) + 1, std::end(data), std::begin(hash)); - if (prefix == payment_address::cashaddr_prefix_mainnet) { - return {hash, type == PUBKEY_TYPE ? payment_address::mainnet_p2kh : payment_address::mainnet_p2sh}; + if (prefix == payment_address::cashaddr_prefix_mainnet) { + return {hash, type == PUBKEY_TYPE ? payment_address::mainnet_p2kh : payment_address::mainnet_p2sh}; + } + return {hash, type == PUBKEY_TYPE ? payment_address::testnet_p2kh : payment_address::testnet_p2sh}; } - return {hash, type == PUBKEY_TYPE ? payment_address::testnet_p2kh : payment_address::testnet_p2sh}; + + if (data.size() == hash_size + 1) { + std::cout << "***** 32 bytes hash" << std::endl; + hash_digest hash; + if ((data.size() - 1) != hash.size()) { + return {}; + } + std::copy(std::begin(data) + 1, std::end(data), std::begin(hash)); + + if (prefix == payment_address::cashaddr_prefix_mainnet) { + return {hash, type == PUBKEY_TYPE ? payment_address::mainnet_p2kh : payment_address::mainnet_p2sh}; + } + return {hash, type == PUBKEY_TYPE ? payment_address::testnet_p2kh : payment_address::testnet_p2sh}; + } + + // Invalid address. + return {}; } #endif //KTH_CURRENCY_BCH @@ -175,7 +240,15 @@ payment_address payment_address::from_string(std::string const& address) { payment decoded; if ( ! decode_base58(decoded, address) || ! is_address(decoded)) { #if defined(KTH_CURRENCY_BCH) - return from_string_cashaddr(address); + auto result = from_string_cashaddr(address); + std::cout << "payment_address::from_string(std::string const& address)" << std::endl; + printf("result.hash().data(): %p\n", result.hash().data()); + std::cout << "result.hash().size(): " << result.hash().size() << std::endl; + for (auto byte : result.hash()) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte); + } + std::cout << std::endl; + return result; #else return {}; #endif //KTH_CURRENCY_BCH @@ -292,25 +365,32 @@ data_chunk pack_addr_data_(T const& id, uint8_t type) { return converted; } -std::string encode_cashaddr_(payment_address const& wallet, bool token_aware) { +std::string encode_cashaddr_(payment_address const& addr, bool token_aware) { // Mainnet - if (wallet.version() == payment_address::mainnet_p2kh || wallet.version() == payment_address::mainnet_p2sh) { + if (addr.version() == payment_address::mainnet_p2kh || addr.version() == payment_address::mainnet_p2sh) { if (token_aware) { return cashaddr::encode(payment_address::cashaddr_prefix_mainnet, - pack_addr_data_(wallet.hash20(), wallet.version() == payment_address::mainnet_p2kh ? TOKEN_PUBKEY_TYPE : TOKEN_SCRIPT_TYPE)); + pack_addr_data_(addr.hash(), addr.version() == payment_address::mainnet_p2kh ? TOKEN_PUBKEY_TYPE : TOKEN_SCRIPT_TYPE)); + } + std::cout << "addr.hash().size(): " << addr.hash().size() << std::endl; + // print the hash in hex format (each byte as 2 chars) + for (auto byte : addr.hash()) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte); } - return cashaddr::encode(payment_address::cashaddr_prefix_mainnet, - pack_addr_data_(wallet.hash20(), wallet.version() == payment_address::mainnet_p2kh ? PUBKEY_TYPE : SCRIPT_TYPE)); + std::cout << std::endl; + return cashaddr::encode( + payment_address::cashaddr_prefix_mainnet, + pack_addr_data_(addr.hash(), addr.version() == payment_address::mainnet_p2kh ? PUBKEY_TYPE : SCRIPT_TYPE)); } // Testnet - if (wallet.version() == payment_address::testnet_p2kh || wallet.version() == payment_address::testnet_p2sh) { + if (addr.version() == payment_address::testnet_p2kh || addr.version() == payment_address::testnet_p2sh) { if (token_aware) { return cashaddr::encode(payment_address::cashaddr_prefix_testnet, - pack_addr_data_(wallet.hash20(), wallet.version() == payment_address::testnet_p2kh ? TOKEN_PUBKEY_TYPE : TOKEN_SCRIPT_TYPE)); + pack_addr_data_(addr.hash(), addr.version() == payment_address::testnet_p2kh ? TOKEN_PUBKEY_TYPE : TOKEN_SCRIPT_TYPE)); } return cashaddr::encode(payment_address::cashaddr_prefix_testnet, - pack_addr_data_(wallet.hash20(), wallet.version() == payment_address::testnet_p2kh ? PUBKEY_TYPE : SCRIPT_TYPE)); + pack_addr_data_(addr.hash(), addr.version() == payment_address::testnet_p2kh ? PUBKEY_TYPE : SCRIPT_TYPE)); } return ""; } @@ -330,10 +410,9 @@ uint8_t payment_address::version() const { return version_; } -//TODO(fernando): re-enable this -// kth::byte_span payment_address::hash() const { -// return hash_span_; -// } +kth::byte_span payment_address::hash() const { + return hash_span_; +} short_hash payment_address::hash20() const { short_hash hash; diff --git a/test/wallet/payment_address.cpp b/test/wallet/payment_address.cpp index 565921c27..79a996be4 100644 --- a/test/wallet/payment_address.cpp +++ b/test/wallet/payment_address.cpp @@ -85,7 +85,7 @@ TEST_CASE("payment address construct secret testnet valid expected", "[payment a // MSVC CTP loses the MSB (WIF prefix) byte of the literal version when // using this initializer, but the MSB isn't used by payment_address. - payment_address const address({secret, 0x806f}); + payment_address const address(ec_private{secret, 0x806f}); REQUIRE(address); REQUIRE(address.encoded_legacy() == ADDRESS_COMPRESSED_TESTNET); } @@ -276,6 +276,24 @@ TEST_CASE("payment address cashAddr testnet from string", "[payment address]") { REQUIRE(address.encoded_cashaddr(false) == "bchtest:qpzz8n7jp6847yyx8t33matrgcsdx6c0cvleatp707"); REQUIRE(address.encoded_legacy() == "mmjF9M1saNs7vjCGaSDaDHvFAtTgUNtfrJ"); } + +TEST_CASE("payment address token address from string", "[payment address]") { + payment_address const address("bitcoincash:pvstqkm54dtvnpyqxt5m5n7sjsn4enrlxc526xyxlnjkaycdzfeu69reyzmqx"); + REQUIRE(address); + + std::cout << "----------------------------------------" << std::endl; + printf("address.hash().data(): %p\n", address.hash().data()); + std::cout << "in the test: " << std::endl; + std::cout << "addr.hash().size(): " << address.hash().size() << std::endl; + for (auto byte : address.hash()) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast(byte); + } + std::cout << std::endl; + + REQUIRE(address.encoded_cashaddr(false) == "bitcoincash:pvstqkm54dtvnpyqxt5m5n7sjsn4enrlxc526xyxlnjkaycdzfeu69reyzmqx"); + REQUIRE(address.encoded_cashaddr(true) == "bitcoincash:pvstqkm54dtvnpyqxt5m5n7sjsn4enrlxc526xyxlnjkaycdzfeu69reyzmqx"); + REQUIRE(address.encoded_legacy() == "17DHrHvtmMRs9ciersFCPNhvJtryd5NWbT"); +} #endif // End Test Suite