From e84242c0304fa6bf0a2fde4c9dd1e199a0be6505 Mon Sep 17 00:00:00 2001 From: chromatic Date: Fri, 14 Jun 2024 21:08:40 -0700 Subject: [PATCH] Add getDerivedHDAddressAsP2PKH function This derives a key from a given public or private key and returns the derived key in P2PKH form. --- include/dogecoin/address.h | 3 +++ src/address.c | 30 ++++++++++++++++++++++++++++++ test/address_tests.c | 26 ++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/include/dogecoin/address.h b/include/dogecoin/address.h index 9700ac7df..9e3e33bc9 100644 --- a/include/dogecoin/address.h +++ b/include/dogecoin/address.h @@ -60,6 +60,9 @@ LIBDOGECOIN_API dogecoin_hdnode* getHDNodeAndExtKeyByPath(const char* masterkey, /* generate an extended hd public/private child address */ LIBDOGECOIN_API int getDerivedHDAddress(const char* masterkey, uint32_t account, bool ischange, uint32_t addressindex, char* outaddress, bool outprivkey); +/* generate an extended hd public/private child address as a P2PKH */ +LIBDOGECOIN_API int getDerivedHDAddressAsP2PKH(const char* masterkey, uint32_t account, bool ischange, uint32_t addressindex, char* outp2pkh); + /* generate an extended hd public/private child address with a more flexible derived path */ LIBDOGECOIN_API int getDerivedHDAddressByPath(const char* masterkey, const char* derived_path, char* outaddress); diff --git a/src/address.c b/src/address.c index 8057bfda6..55731ae7d 100644 --- a/src/address.c +++ b/src/address.c @@ -490,6 +490,36 @@ int getDerivedHDAddress(const char* masterkey, uint32_t account, bool ischange, return ret; } +/** + * @brief This function generates a derived child address from a masterkey using + * a BIP44 standardized static, non hardened path comprised of an account, a change or + * receiving address and an address index. + * + * @param masterkey The master key from which children are derived from. + * @param account The account that the derived address would belong to. + * @param ischange Boolean value representing either a change or receiving address. + * @param addressindex The index of the receiving/change address per account. + * @param outp2pkh The derived address in P2PSH form. + * + * @return 1 if a derived address was successfully generated, 0 otherwise. + */ +int getDerivedHDAddressAsP2PKH(const char* masterkey, uint32_t account, bool ischange, uint32_t addressindex, char* outp2pkh) { + if (!masterkey) { + debug_print("%s", "no extended key\n"); + return false; + } + + char derived_path[DERIVED_PATH_STRINGLEN]; + int derived_path_size = snprintf(derived_path, sizeof(derived_path), "m/44'/3'/%u'/%u/%u", account, ischange, addressindex); + + if (derived_path_size >= (int)sizeof(derived_path)) { + debug_print("%s", "derivation path overflow\n"); + return false; + } + + return getDerivedHDAddressByPath(masterkey, derived_path, outp2pkh); +} + /** * @brief This function generates a new dogecoin address from a mnemonic by the slip44 key path. * diff --git a/test/address_tests.c b/test/address_tests.c index 7c674a287..f09b90610 100644 --- a/test/address_tests.c +++ b/test/address_tests.c @@ -134,6 +134,32 @@ void test_address() u_assert_int_eq(res, true); u_assert_str_eq(extout, "dgub8wfrZMXz8ojFcPziSubEoQ65sB4PYPyYTMo3PqFwf2Vx5zZ6ia17Nk2Py25c3dvq1e7ZnfBrurCS5wuagzRoBCXhJ2NeGU54NBytvuUuRyA"); + /* ckd p2pkh generation */ + res = getDerivedHDAddressAsP2PKH(masterkey_main_ext, 0, false, 0, extout); + u_assert_int_eq(res, true); + u_assert_str_eq(extout, "DCm7oSg95sxwn3sWxYUDHgKKbB2mDmuR3B"); + res = getDerivedHDAddressAsP2PKH(masterkey_main_ext, 0, true, 0, extout); + u_assert_int_eq(res, true); + u_assert_str_eq(extout, "D91jVi3CVGhRmyt83fhMdL4UJWtDuiTZET"); + res = getDerivedHDAddressAsP2PKH(masterkey_main_ext, 0, false, 0, extout); + u_assert_int_eq(res, true); + u_assert_str_eq(extout, "DCm7oSg95sxwn3sWxYUDHgKKbB2mDmuR3B"); + res = getDerivedHDAddressAsP2PKH(masterkey_main_ext, 0, true, 0, extout); + u_assert_int_eq(res, true); + u_assert_str_eq(extout, "D91jVi3CVGhRmyt83fhMdL4UJWtDuiTZET"); + res = getDerivedHDAddressAsP2PKH(masterkey_main_ext, 1, false, 1, extout); + u_assert_int_eq(res, true); + u_assert_str_eq(extout, "D5Se361tds246n9Bm6diMQwkg7PfQrME65"); + res = getDerivedHDAddressAsP2PKH(masterkey_main_ext, 1, true, 1, extout); + u_assert_int_eq(res, true); + u_assert_str_eq(extout, "DD5ztaSL3pscXYL6XXcRFTvbdghKppsKDn"); + res = getDerivedHDAddressAsP2PKH(masterkey_main_ext, 1, false, 1, extout); + u_assert_int_eq(res, true); + u_assert_str_eq(extout, "D5Se361tds246n9Bm6diMQwkg7PfQrME65"); + res = getDerivedHDAddressAsP2PKH(masterkey_main_ext, 1, true, 1, extout); + u_assert_int_eq(res, true); + u_assert_str_eq(extout, "DD5ztaSL3pscXYL6XXcRFTvbdghKppsKDn"); + // hardened paths (unabstracted as this is called by getDerivedHDAddress) res = getDerivedHDKeyByPath(masterkey_main_ext, "m/44'/3'/0'/0/0", extout, true); u_assert_int_eq(res, true);