Skip to content

Commit

Permalink
Renamed HD key conversion functions with more sensible names.
Browse files Browse the repository at this point in the history
The old name `generateDerivedHDPubKey` is misleading - it doesn't
generate anything new, it just converts the HD pubkey to P2PKH format.
It also works for any HD child address, so added that to docs.

Backwards compatibility: there's a 1-line wrapper function with the
old name, to maintain binary compatibility.

Also renamed my `generateDerivedHDPrivKeyWIF` (which was named that
way to be consistent!) - it just converts the HD privkey to a
non-extended non-HD private key (EC Privkey)

The EC Privkey is required to sign transactions, since you can't
sign a transaction using an extended HD key, the API doesn't support it.
  • Loading branch information
raffecat committed Aug 1, 2023
1 parent 8352b0a commit 46a51b6
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 66 deletions.
52 changes: 29 additions & 23 deletions doc/address.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
- [Essential Address API](#essential-address-api)
- [**generatePrivPubKeypair:**](#generateprivpubkeypair)
- [**generateHDMasterPubKeypair:**](#generatehdmasterpubkeypair)
- [**generateDerivedHDPubKey:**](#generatederivedhdpubkey)
- [**convertHDKeyToP2PKH:**](#convertHDKeyToP2PKH)
- [**convertHDKeyToECPrivKey:**](#convertHDKeyToECPrivKey)
- [**verifyPrivPubKeypair**](#verifyprivpubkeypair)
- [**verifyHDMasterPubKeypair**](#verifyhdmasterpubkeypair)
- [**verifyP2pkhAddress**](#verifyp2pkhaddress)
Expand Down Expand Up @@ -144,14 +145,16 @@ int main() {

---

### **generateDerivedHDPubKey:**
### **convertHDKeyToP2PKH:**

`int generateDerivedHDPubkey(const char* wif_privkey_master, char* p2pkh_pubkey)`
`int convertHDKeyToP2PKH(const char* extended_key, char* out_p2pkh_pubkey)`

This function takes a given HD master private key (wif_privkey_master) and loads it into the provided pointer for the resulting derived public key (p2pkh_pubkey). This private key input should come from the result of generateHDMasterPubKeypair(). The function returns 1 on success and 0 on failure.

This function also works with any extended HD public address (e.g. dgub, tpub) from getDerivedHDAddress
or getDerivedHDAddressByPath, to get the corresponding public P2PKH Address (Dogecoin Address)
* **extended_key** The HD masterkey or HD child address (public or private)
* **out_p2pkh_pubkey** char[] for the corresponding P2PKH public key, at least P2PKH_ADDR_STRINGLEN chars.

This function takes an HD masterkey or HD child address (public or private) and converts its public key
to a P2PKH Address (Dogecoin Address.) The extended_key can come from generateHDMasterPubKeypair,
getDerivedHDAddress or getDerivedHDAddressByPath. The function returns 1 on success and 0 on failure.

_C usage:_

Expand All @@ -160,36 +163,39 @@ _C usage:_
#include <stdio.h>

int main() {
int masterPrivkeyLen = 200; // enough cushion
int pubkeyLen = 35;

char masterPrivKey[masterPrivkeyLen];
char masterPubKey[pubkeyLen];
char childPubKey[pubkeyLen];
char masterPrivKey[HD_MASTERKEY_STRINGLEN];
char masterPubKey[P2PKH_ADDR_STRINGLEN];
char childHDPublic[HD_MASTERKEY_STRINGLEN];
char childP2PKHAddress[P2PKH_ADDR_STRINGLEN];

dogecoin_ecc_start();
generateHDMasterPubKeypair(masterPrivKey, masterPubKey, false);
generateDerivedHDPubkey(masterPrivKey, childPubKey);
getDerivedHDAddress(masterPrivKey, 0, false, 1, childHDPublic, false);
convertHDKeyToP2PKH(childHDPublic, childP2PKHAddress);
dogecoin_ecc_stop();

printf("master private key: %s\n", masterPrivKey);
printf("master public key: %s\n", masterPubKey);
printf("derived child key: %s\n", childPubKey);
printf("derived child key: %s\n", childHDPublic);
printf("derived child address: %s\n", childP2PKHAddress);
}
```

---

### **generateDerivedHDPrivKeyWIF**
### **convertHDKeyToECPrivKey**

`int convertHDKeyToECPrivKey(const char* extended_private, char* out_ec_privkey)`

`int generateDerivedHDPrivKeyWIF(const char* extended_private, char* out_privkey_wif)`
* **extended_private** The extended HD private key (e.g. dgpv, tprv)
* **out_ec_privkey** char[] for the WIF-encoded private EC key, at least WIF_UNCOMPRESSED_PRIVKEY_STRINGLEN chars.

This function converts an extended HD private address (e.g. dgpv, tprv) to a WIF-encoded private key.
This function converts an extended private key (e.g. dgpv, tprv) to an EC private key in WIF format.

It works with any extended HD private address from getDerivedHDAddress or getDerivedHDAddressByPath,
to get the corresponding WIF-encoded private key for the child address.
to get the corresponding WIF-encoded EC private key for the child address.

You will need a WIF-encoded private key to sign transactions.
You will need a WIF-encoded EC private key to sign transactions.

_C usage:_

Expand All @@ -201,18 +207,18 @@ int main() {
char masterPrivKey[HD_MASTERKEY_STRINGLEN];
char masterPubKey[P2PKH_ADDR_STRINGLEN];
char childHDPrivate[HD_MASTERKEY_STRINGLEN];
char privkeyWIF[WIF_UNCOMPRESSED_PRIVKEY_STRINGLEN];
char ECprivkey[WIF_UNCOMPRESSED_PRIVKEY_STRINGLEN];

dogecoin_ecc_start();
generateHDMasterPubKeypair(masterPrivKey, masterPubKey, false);
getDerivedHDAddress(masterPrivKey, 0, false, 1, childHDPrivate, true);
generateDerivedHDPrivKeyWIF(childHDPrivate, privkeyWIF);
convertHDKeyToECPrivKey(childHDPrivate, ECprivkey);
dogecoin_ecc_stop();

printf("master private key: %s\n", masterPrivKey);
printf("master public key: %s\n", masterPubKey);
printf("derived child key: %s\n", childHDPrivate);
printf("derived privkey WIF: %s\n", privkeyWIF);
printf("derived privkey WIF: %s\n", ECprivkey);
}
```

Expand Down
11 changes: 7 additions & 4 deletions include/dogecoin/address.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ LIBDOGECOIN_API int generatePrivPubKeypair(char* wif_privkey, char* p2pkh_pubkey
/* generate HD master key and WIF public key */
LIBDOGECOIN_API int generateHDMasterPubKeypair(char* wif_privkey_master, char* p2pkh_pubkey_master, bool is_testnet);

/* generate an extended public key */
LIBDOGECOIN_API int generateDerivedHDPubkey(const char* wif_privkey_master, char* p2pkh_pubkey);
/* converts an HD masterkey or HD child address (public or private) to P2PKH Dogecoin Address */
LIBDOGECOIN_API int convertHDKeyToP2PKH(const char* extended_key, char* out_p2pkh_pubkey);

/* converts an HD private key (e.g. dgpv, tprv) to a WIF-encoded EC private key. */
LIBDOGECOIN_API int convertHDKeyToECPrivKey(const char* extended_private, char* out_ec_privkey);

/* converts an extended HD private address (dgpv, tprv) to a WIF-encoded private key */
LIBDOGECOIN_API int generateDerivedHDPrivKeyWIF(const char* extended_private, char* out_privkey_wif);
/* deprecated: renamed to convertHDKeyToP2PKH */
LIBDOGECOIN_API int generateDerivedHDPubkey(const char* wif_privkey_master, char* p2pkh_pubkey);

/* verify private and public keys are valid and associated with each other*/
LIBDOGECOIN_API int verifyPrivPubKeypair(char* wif_privkey, char* p2pkh_pubkey, bool is_testnet);
Expand Down
15 changes: 9 additions & 6 deletions include/dogecoin/libdogecoin.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,20 @@ void dogecoin_ecc_start(void);
//!destroys the static ecc context
void dogecoin_ecc_stop(void);

/* generates a private and public keypair (a wallet import format private key and a p2pkh ready-to-use corresponding dogecoin address)*/
/* generates a private and public keypair (a wallet import format EC private key and a p2pkh ready-to-use corresponding dogecoin address)*/
int generatePrivPubKeypair(char* wif_privkey, char* p2pkh_pubkey, bool is_testnet);

/* generates a hybrid deterministic WIF master key and p2pkh ready-to-use corresponding dogecoin address. */
/* generates a hybrid deterministic (HD) WIF master key and p2pkh ready-to-use corresponding dogecoin address. */
int generateHDMasterPubKeypair(char* wif_privkey_master, char* p2pkh_pubkey_master, bool is_testnet);

/* generates a new dogecoin address from a HD master key */
int generateDerivedHDPubkey(const char* wif_privkey_master, char* p2pkh_pubkey);
/* converts an HD masterkey or HD child address (public or private) to P2PKH Dogecoin Address */
int convertHDKeyToP2PKH(const char* extended_key, char* out_p2pkh_pubkey);

/* converts an HD private key (e.g. dgpv, tprv) to a WIF-encoded EC private key. */
int convertHDKeyToECPrivKey(const char* extended_private, char* out_ec_privkey);

/* converts an extended HD private address (dgpv, tprv) to a WIF-encoded private key */
int generateDerivedHDPrivKeyWIF(const char* extended_private, char* out_privkey_wif);
/* deprecated: renamed to convertHDKeyToP2PKH */
int generateDerivedHDPubkey(const char* wif_privkey_master, char* p2pkh_pubkey);

/* verify that a private key and dogecoin address match */
int verifyPrivPubKeypair(char* wif_privkey, char* p2pkh_pubkey, bool is_testnet);
Expand Down
56 changes: 28 additions & 28 deletions src/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ int generateHDMasterPubKeypair(char* wif_privkey_master, char* p2pkh_pubkey_mast
return false;
}

if (!generateDerivedHDPubkey(hd_privkey_master, hd_pubkey_master)) {
if (!convertHDKeyToP2PKH(hd_privkey_master, hd_pubkey_master)) {
return false;
}

Expand All @@ -161,61 +161,61 @@ int generateHDMasterPubKeypair(char* wif_privkey_master, char* p2pkh_pubkey_mast
}

/**
* @brief This function takes a wif-encoded HD master
* private key and derive a new HD public key from it
* on the specified network. This input should come from
* the result of generateHDMasterPubKeypair().
* @brief This function takes an extended HD key and converts its
* public key to P2PKH format. The extended_key can come from
* generateHDMasterPubKeypair, getDerivedHDAddress or getDerivedHDAddressByPath.
*
* @param wif_privkey_master The master private key to derive the child key from.
* @param p2pkh_pubkey The resulting child public key.
* @param extended_key The HD masterkey or HD child address (public or private)
* @param out_p2pkh_pubkey char[] for the corresponding P2PKH public key,
* at least P2PKH_ADDR_STRINGLEN chars
*
* @return 1 if the child key was generated successfully, 0 otherwise.
* @return 1 if the P2PKH pubkey was generated successfully, 0 otherwise.
*/
int generateDerivedHDPubkey(const char* wif_privkey_master, char* p2pkh_pubkey)
int convertHDKeyToP2PKH(const char* extended_key, char* out_p2pkh_pubkey)
{
/* require master key */
if (!wif_privkey_master) {
if (!extended_key) {
return false;
}

/* determine address prefix for network chainparams */
const dogecoin_chainparams* chain = chain_from_b58_prefix(wif_privkey_master);
const dogecoin_chainparams* chain = chain_from_b58_prefix(extended_key);

char str[P2PKH_ADDR_STRINGLEN];

/* if nothing is passed in use internal variables */
if (p2pkh_pubkey) {
memcpy_safe(str, p2pkh_pubkey, sizeof(str));
dogecoin_hdnode node;
if (!dogecoin_hdnode_deserialize(extended_key, chain, &node)) {
return false;
}

dogecoin_hdnode* node = dogecoin_hdnode_new();
dogecoin_hdnode_deserialize(wif_privkey_master, chain, node);

dogecoin_hdnode_get_p2pkh_address(node, chain, str, sizeof(str));
dogecoin_hdnode_get_p2pkh_address(&node, chain, str, sizeof(str));

/* pass back to external variable if exists */
if (p2pkh_pubkey) {
memcpy_safe(p2pkh_pubkey, str, sizeof(str));
if (out_p2pkh_pubkey) {
memcpy_safe(out_p2pkh_pubkey, str, sizeof(str));
}

/* reset internal variables */
dogecoin_hdnode_free(node);
dogecoin_mem_zero(str, strlen(str));
dogecoin_hdnode_clear(&node);

return true;
}

/* for backwards compatiblity with existing library users */
int generateDerivedHDPubkey(const char* extended_key, char* p2pkh_pubkey) {
return convertHDKeyToP2PKH(extended_key, p2pkh_pubkey);
}

/**
* @brief This function converts an HD extended private address
* (e.g. dgpv, tprv) to a WIF-encoded private key.
* (e.g. dgpv, tprv) to a WIF-encoded private EC key.
*
* @param extended_private The extended HD private key (e.g. dgpv, tprv)
* @param out_privkey_wif output buffer for the WIF-encoded private key,
* at least WIF_UNCOMPRESSED_PRIVKEY_STRINGLEN chars
* @param out_ec_privkey char[] for the WIF-encoded private EC key,
* at least WIF_UNCOMPRESSED_PRIVKEY_STRINGLEN chars
*
* @return dogecoin_bool (uint8_t)
*/
int generateDerivedHDPrivKeyWIF(const char* extended_private, char* out_privkey_wif)
int convertHDKeyToECPrivKey(const char* extended_private, char* out_ec_privkey)
{
const dogecoin_chainparams* chain = chain_from_bip32_prefix(extended_private);
if (!chain) {
Expand All @@ -228,7 +228,7 @@ int generateDerivedHDPrivKeyWIF(const char* extended_private, char* out_privkey_
size_t out_size = WIF_UNCOMPRESSED_PRIVKEY_STRINGLEN;
dogecoin_key key;
memcpy_safe(key.privkey, node.private_key, sizeof(key.privkey));
dogecoin_privkey_encode_wif(&key, chain, out_privkey_wif, &out_size);
dogecoin_privkey_encode_wif(&key, chain, out_ec_privkey, &out_size);
dogecoin_privkey_cleanse(&key);
dogecoin_hdnode_clear(&node);
return true;
Expand Down
15 changes: 10 additions & 5 deletions test/address_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,16 @@ void test_address()
u_assert_str_eq(p2pkh_pubkey_main, "DTdKu8YgcxoXyjFCDtCeKimaZzsK27rcwT");
#endif

/* generateDerivedHDPrivKeyWIF */
const char* ext_hd_private = "tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK";
char privkey_wif_out[WIF_UNCOMPRESSED_PRIVKEY_STRINGLEN];
u_assert_int_eq(generateDerivedHDPrivKeyWIF(ext_hd_private, privkey_wif_out), true);
u_assert_str_eq(privkey_wif_out, "chq4gQqR1C5erpniafsiu581zFCrctDiUMPTHiMWD3h6H9WWrMzu");
/* convertHDKeyToP2PKH */
u_assert_int_eq(convertHDKeyToP2PKH("dgpv51eADS3spNJhA6LG5QycrFmQQtxg7ztFJQuamYiytZ4x4FUC7pG5B7fUTHBDB7g6oGaCVwuGF2i75r1DQKyFSauAHUGBAi89NaggpdUP3yK", str), true)
u_assert_str_eq("DEByFfUQ3AxcFFet9afr8wxxedQysRduWN", str);
u_assert_int_eq(convertHDKeyToP2PKH("dgub8uxGyZKCxRo2buadqKBPGR5MMDrbk8RABK8EcnBv5GrdS8u1Lw2ifRSifsT3wuVRsK45b9kugWkd2cREzkJLiGvwbY5txG2dKfsY3bndC93", str), true)
u_assert_str_eq("D91jVi3CVGhRmyt83fhMdL4UJWtDuiTZET", str);

/* convertHDKeyToECPrivKey */
char privkey[WIF_UNCOMPRESSED_PRIVKEY_STRINGLEN];
u_assert_int_eq(convertHDKeyToECPrivKey("tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK", privkey), true);
u_assert_str_eq("chq4gQqR1C5erpniafsiu581zFCrctDiUMPTHiMWD3h6H9WWrMzu", privkey);

/*free up VLAs*/
free(masterkey_main);
Expand Down

0 comments on commit 46a51b6

Please sign in to comment.