meherett / python-hdwallet

Python-based library for the implementation of a hierarchical deterministic wallet generator for more than 140+ multiple cryptocurrencies.
https://hdwallet.readthedocs.io
MIT License
458 stars 150 forks source link

difference between address keys #59

Closed sobhan-m94 closed 2 years ago

sobhan-m94 commented 2 years ago

what is the difference between root_xprivate_key and xprivate_key OR root_xpublic_key and xpublic_key ? where is account extended private key and account extended public key ?

{
    "cryptocurrency": "Bitcoin",
    "symbol": "BTC",
    "network": "mainnet",
    "strength": 160,
    "entropy": "c5b0d0ee698f3f72b6265f1bc591f8f2d7afa6dd",
    "mnemonic": "주일 액수 명단 천둥 해수욕장 전망 추천 직업 그룹 단위 신체 파란색 시청 천천히 스트레스",
    "language": "korean",
    "passphrase": null,
    "seed": "5a9b9667ccd07b3c641b1ba95e9119dd1d5a3034fd46cd2f27fc1f160c7dcd824fc0ab4710a9ae90582dffc3b0803bcbc0a8160feeaab4c70511c5035859decf",
    "root_xprivate_key": "xprv9s21ZrQH143K2qMHU8aghJ4MoQR5g5mowXbeP2vCP937bseZGX929dmJudL7u4xRxtKvh58pxz1PhtCbWW2yUH14jdduKVMV9FkBMpM2Hyw",
    "root_xpublic_key": "xpub661MyMwAqRbcFKRkaA7h4S16MSFa5YVfJkXFBRKowUa6Ufyhp4TGhS5nkvkLXSmdNjoszzDkU26WW2rg1zBsQBt6Pv3T8oLEAExGHD3hcQs",
    "xprivate_key": "xprvA2YyMZWyPK2xo4eZgyypp2CzcHnxNzGbruGg7vmgaAVCtBtrjwzuhXJBNM3FrwBh85ajxHErNR6ByN77WJARpC1HDC7kTwa2yr7Mu9Pz5Qq",
    "xpublic_key": "xpub6FYKm53sDgbG1Yj2o1WqBA9jAKdSnSzTE8CGvKBJ8W2BkzE1HVKAFKcfDcCHKpL5BQRg2HjbNSt55jpFshY7W1KFtp7zjB3DhNAmiFv6kzB",
    "uncompressed": "081016370b45d7e23bd89b07d6886036f5e4df9a129eee3b488c177ba7881856e24d337b280f9d32539a22445e567543b39b708edf5289442f36dcde958a3433",
    "compressed": "03081016370b45d7e23bd89b07d6886036f5e4df9a129eee3b488c177ba7881856",
    "chain_code": "cf9ee427ed8073e009a5743056e8cf19167f67ca5082c2c6635b391e9a4e0b0d",
    "private_key": "f79495fda777197ce73551bcd8e162ceca19167575760d3cc2bced4bf2a213dc",
    "public_key": "03081016370b45d7e23bd89b07d6886036f5e4df9a129eee3b488c177ba7881856",
    "wif": "L5WyVfBu8Sz3iGZtrwJVSP2wDJmu7HThGd1EGekFBnviWgzLXpJd",
    "finger_print": "ac13e305",
    "semantic": "p2pkh",
    "path": "m/44'/0'/0'/0/0",
    "hash": "ac13e305a88bd9968f1c058fcf5d9a6b1b9ef484",
    "addresses": {
        "p2pkh": "1Ggs3kkNrPPWoW17iDFQWgMdw3CD8BzBiv",
        "p2sh": "3GQVUFePz517Hf61Vsa9H2tHj5jw5y6ngV",
        "p2wpkh": "bc1q4sf7xpdg30vedrcuqk8u7hv6dvdeaayy3uw5cj",
        "p2wpkh_in_p2sh": "3JyV5aSgdVYEjQodPWHfvehQ5227EDr3sN",
        "p2wsh": "bc1qnk0s9q4379n6v9vg0lnhdu5qhjyx99u2xm238pmckmjg9v29q54saddzp9",
        "p2wsh_in_p2sh": "3MmsEoP7GLHzuLVgkAtcRtyXLTWh8zNAcd"
    }
}
meherett commented 2 years ago

Hey @smm993,

For the 1st question: Okay both are the same keys but the difference is when you come to root_x(private/public)_key is not derived key but x(private/public)_key is already derived by path/indexes value.

You can also check both root keys on the above keys

from hdwallet.utils import is_root_xprivate_key, is_root_xpublic_key

# Checking root xprivate key
print(is_root_xprivate_key(xprivate_key="xprv9s21ZrQH143K2qMHU8aghJ4MoQR5g5mowXbeP2vCP937bseZGX929dmJudL7u4xRxtKvh58pxz1PhtCbWW2yUH14jdduKVMV9FkBMpM2Hyw", symbol="BTC"))
# True
print(is_root_xprivate_key(xprivate_key="xprvA2YyMZWyPK2xo4eZgyypp2CzcHnxNzGbruGg7vmgaAVCtBtrjwzuhXJBNM3FrwBh85ajxHErNR6ByN77WJARpC1HDC7kTwa2yr7Mu9Pz5Qq", symbol="BTC"))
# False

# Checking root xpublic key
print(is_root_xpublic_key(xpublic_key="xpub661MyMwAqRbcFKRkaA7h4S16MSFa5YVfJkXFBRKowUa6Ufyhp4TGhS5nkvkLXSmdNjoszzDkU26WW2rg1zBsQBt6Pv3T8oLEAExGHD3hcQs", symbol="BTC")) 
# True
print(is_root_xpublic_key(xpublic_key="xpub6FYKm53sDgbG1Yj2o1WqBA9jAKdSnSzTE8CGvKBJ8W2BkzE1HVKAFKcfDcCHKpL5BQRg2HjbNSt55jpFshY7W1KFtp7zjB3DhNAmiFv6kzB", symbol="BTC"))
# False

For the 2nd question: To get account extended private/public keys; you have to understand the path-levels after that to get account extended keys; when you generate hdwallet, you have to stop the derivation path/indexes at account index then you can get it.

So, to get account extended keys you have to use this path

m / purpose' / coin_type' / account' 

For example, to get account extended keys on above generated hdwallet

{
    "cryptocurrency": "Bitcoin",
    "symbol": "BTC",
    "network": "mainnet",
    "strength": 160,
    "entropy": "c5b0d0ee698f3f72b6265f1bc591f8f2d7afa6dd",
    "mnemonic": "주일 액수 명단 천둥 해수욕장 전망 추천 직업 그룹 단위 신체 파란색 시청 천천히 스트레스",
    "language": "korean",
    "passphrase": null,
    "seed": "5a9b9667ccd07b3c641b1ba95e9119dd1d5a3034fd46cd2f27fc1f160c7dcd824fc0ab4710a9ae90582dffc3b0803bcbc0a8160feeaab4c70511c5035859decf",
    "root_xprivate_key": "xprv9s21ZrQH143K2qMHU8aghJ4MoQR5g5mowXbeP2vCP937bseZGX929dmJudL7u4xRxtKvh58pxz1PhtCbWW2yUH14jdduKVMV9FkBMpM2Hyw",
    "root_xpublic_key": "xpub661MyMwAqRbcFKRkaA7h4S16MSFa5YVfJkXFBRKowUa6Ufyhp4TGhS5nkvkLXSmdNjoszzDkU26WW2rg1zBsQBt6Pv3T8oLEAExGHD3hcQs",
    "xprivate_key": "xprv9yZDeH96FLVfvvcihoZUQC8PYsiRzC6QTfyZ93GSBcVXWd5f2rRrfB9UBRtYFwPHtQzwjjoUNzuL9LNdeyX24jnNjsUgQf9VCUC3A7GjXnv",
    "xpublic_key": "xpub6CYa3nfz5i3y9QhBoq6UmL586uYvPepFptu9wRg3jx2WPRQoaPk7CyTx2h3rK5yKMwCGT5a3si8xARmoFKDWDhXK5PrN8D67QqfhJodcLvh",
    "uncompressed": "34dc1719aa67271d9fbca5d16731259546827dd5c2195a0faf0cef262de1c547f70c83d4c4e7386b416940ff782d0c07eae57a207dd83552c3badc071d4ccbee",
    "compressed": "0234dc1719aa67271d9fbca5d16731259546827dd5c2195a0faf0cef262de1c547",
    "chain_code": "49d06dafa9c930c7f28a89ee642643f7125a1d1520af23361eeea74e3596a2e4",
    "private_key": "23ba50c0af0a3c6e36b3b8ac31e3ffbba496ca1d33663c47c0ca7a27b69e5687",
    "public_key": "0234dc1719aa67271d9fbca5d16731259546827dd5c2195a0faf0cef262de1c547",
    "wif": "KxRAJ1ZAPWwgg6Q4KZyuVM9K1FdszrgbFphJCvGRu3QHq8HBA5Hq",
    "finger_print": "eee64ac8",
    "semantic": "p2pkh",
    "path": "m/44'/0'/0'",
    "hash": "eee64ac8c8c54998a077814792d595e418db2f6a",
    "addresses": {
        "p2pkh": "1NnBhdihfXbjDz4fAmETp38ewYc2xGFhYb",
        "p2sh": "3FLJC2ygV4MuvTTdYkiDqQ4v7CLvpGsFfz",
        "p2wpkh": "bc1qamny4jxgc4ye3grhs9re94v4usvdktm2sl5zwq",
        "p2wpkh_in_p2sh": "3Ppx4CcxLN9Wn4MQp4TYu7Yf8JVWAuD4A7",
        "p2wsh": "bc1ql8m4kepda9v6f87pkxqam847nmf8qhdqfkmlpnth7a4tfl8kjheq887a20",
        "p2wsh_in_p2sh": "3BZqrUnm1RFuE61oc1D742QwqFkMXpQMGA"
    }
}

Both xprivate_key and xpublic_key are now account extended private key and account extended public key.

For more test and check out here https://iancoleman.io/bip39 image

Thanks!

sobhan-m94 commented 2 years ago

What about this?

from hdwallet import BIP44HDWallet
from hdwallet.cryptocurrencies import TronMainnet
from typing import Optional
import json

hdwallet: BIP44HDWallet = BIP44HDWallet(cryptocurrency=TronMainnet)
hdwallet.from_mnemonic(
    mnemonic='imitate minute bulk rapid eager captain case envelope submit bike birth unfold', language="english", passphrase=None
)
hdwallet.clean_derivation()
hdwallet.from_index(44, hardened=True)
hdwallet.from_index(195, hardened=True)
hdwallet.from_index(0, hardened=True)
hdwallet.from_index(0)
hdwallet.from_index(0)
print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))
{
    "cryptocurrency": "Tron",
    "symbol": "TRX",
    "network": "mainnet",
    "strength": 128,
    "entropy": "7171a87858f44e4448ca5ed802c45af6",
    "mnemonic": "imitate minute bulk rapid eager captain case envelope submit bike birth unfold",
    "language": "english",
    "passphrase": null,
    "seed": "be5b3d22a5b56346d5b2e8b80190a16c266fe87ff7ff45317422423b4d0632ab2f2a4ae8c0905d85a2799ecf85e601e03de08c68e307ab8f2a7904b4ee53f343",
    "root_xprivate_key": "xprv9s21ZrQH143K3jfX39z2VtxEXNbjtv6Yi7nhfKqmc3XUNTrkKwWAWpTfBPTgz8e5wcWEcq2P7Es6t78gHsHx96HxuzYCqz8uSmVfnV6pY3k",
    "root_xpublic_key": "xpub661MyMwAqRbcGDjz9BX2s2ty5QSEJNpQ5LiJTiFPAP4TFGBtsUpR4cn92hMUZACfgSu5NPPtGPsFPZU97fPFRQF5MQSSZwkr3Xrq4zy8CHA",
    "xprivate_key": "xprvA2dttGszXzcuWpMwk4pTF31AgHX1JmCPvEsjWbSUiXXqmjhE2WRakeyvGTdAEtBF6MmTjXfmBsKV9g6KLE4Cda7DYS3M4vdEA8ehFyvuuPi",
    "xpublic_key": "xpub6FdFHnQtNNBCjJSQr6MTcAwuEKMViDvFHToLJyr6Gs4peY2Na3jqJTJQ7jCScix5CHngFM1RMAzkzTjhhVsKZUWzToPqy3i5achTNWfGxUL",
    "uncompressed": "1d5c4eb2954afeb2bde7f277adb6a5928d6a827e3b61ba71635ae65908841285de6aec82fa32601fe5e935bcfc3b2a103f0c4b412acc64ebc25ec85beb880d5d",
    "compressed": "031d5c4eb2954afeb2bde7f277adb6a5928d6a827e3b61ba71635ae65908841285",
    "chain_code": "63b49b7da86d3956eb0839aea3bd475d1b64e4956af564d371ee67fdc4890f2e",
    "private_key": "d5cca4a23c07b680c2d843e440815f0848acb09a7d884b7f64ee3ff1050282fb",
    "public_key": "031d5c4eb2954afeb2bde7f277adb6a5928d6a827e3b61ba71635ae65908841285",
    "wif": "L4PJsZr7HDHUxJJ4FMfDuSvCZAWdWwiWVgBRTKuvt8sxsebYBAhZ",
    "finger_print": "ede0a921",
    "semantic": "p2pkh",
    "path": "m/44'/195'/0'/0/0",
    "hash": "ede0a921abcc9463ad7dcfdcd76d8dba2a721276",
    "addresses": {
        "p2pkh": "TYM2d84UfSC5uRR9EuTexjZ9sGzeu9HTZh",
        "p2sh": "3MBUWhzVJmE2trmoVAZyM8D1ER3TG9aphc",
        "p2wpkh": "bc1qahs2jgdtej2x8ttaelwdwmvdhg48yynkaec9mg",
        "p2wpkh_in_p2sh": "3ET69JrnaQrXfLrRDncuc2stPFPBNHNLmR",
        "p2wsh": "bc1qaju7kgk5uqnml3treqe7pmcaxfg98zs9y9qzt46z8n8vh5sjldwqa4zgmf",
        "p2wsh_in_p2sh": "34ajXFn4oipFzLoeMBBu6MMuw8mjxzbYKX"
    }
}

Can you give me sample in python to get account extended private/public keys ? Also bip32 extended private/public keys.

meherett commented 2 years ago

Just remove the last two derivation indexes from

hdwallet.from_index(44, hardened=True)  # purpose
hdwallet.from_index(195, hardened=True)  # coin_type
hdwallet.from_index(0, hardened=True)  # account
hdwallet.from_index(0)  # change (external/internal)
hdwallet.from_index(0)  # address_index

to

hdwallet.from_index(44, hardened=True)  # purpose
hdwallet.from_index(195, hardened=True)  # coin_type
hdwallet.from_index(0, hardened=True)  # account

then you will get account extended private/public keys.

sobhan-m94 commented 2 years ago

Imagine I have a website to sell things. I need the website(API) to be able to generate addresses, but I don't want it to be able to spend from them. With which key should I generate the addresses? xpublic_key ? and how can I get private key of addresses generated by API (xpublic_key ) ?

meherett commented 2 years ago

It's depend on you. But, my recommendation is use root xprivate and xpublic keys. You can drive it in what ever you want derivations.

And also to generate addresses use BIP44 default path because most of wallets are uses BIP44 default paths. For example MetaMask wallet to generate addresses; it uses this Ethereum m/44'/60'/0'/0/0 default BIP44 path.

sobhan-m94 commented 2 years ago

my recommendation is use root xprivate and xpublic keys.

generate addresses use BIP44 default path.

bip44 default path is hardened. we cant generate addresses with xpublic key.

meherett commented 2 years ago

Yes man, BIP44 requires hardened keys. In BIP44 purpose, coin_type and account are hardened keys and also all are requires xprivate key for derivation but change (external/internal) and address_index are not hardened keys and both are not requires xprivate key for derivation. So, in this case; if you want to use BIP44; my recommendation is use account extended (xprivate and xpublic keys). after you get both account extended x keys then you can generate addresses by account extended xpublic key.

I will consider to add account extended x keys on BIP44HDWallet but it's very slow down the generation time.

sobhan-m94 commented 2 years ago

after you get both account extended x keys then you can generate addresses by account extended xpublic key.

I cant generate addresses by account extended xpublic key.

from hdwallet import BIP44HDWallet
from hdwallet.cryptocurrencies import TronMainnet
from hdwallet import HDWallet as HDWallet
from hdwallet.symbols import TRX as SYMBOL
import json

hdwallet: BIP44HDWallet = BIP44HDWallet(cryptocurrency=TronMainnet)
hdwallet.from_mnemonic(
    mnemonic='imitate minute bulk rapid eager captain case envelope submit bike birth unfold', language="english", passphrase=None
)
hdwallet.clean_derivation()
hdwallet.from_index(44, hardened=True)
hdwallet.from_index(195, hardened=True)
hdwallet.from_index(0, hardened=True)

xpublic_key=hdwallet.xpublic_key()

hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
hdwallet.from_xpublic_key(xpublic_key=xpublic_key)
hdwallet.clean_derivation()

hdwallet.from_index(44, hardened=True)
hdwallet.from_index(195, hardened=True)
hdwallet.from_index(0, hardened=True)
hdwallet.from_index(0)
hdwallet.from_index(0)

print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))

Error :

hdwallet.exceptions.DerivationError: Hardened derivation path is invalid for xpublic key.

meherett commented 2 years ago

The error is correct because, Hardened key requires xprivate key but you have only xpublic key.

After you get account extended xpublic key, that means you already dived it by purpose, coin_type and account hardened keys. So now, in BIP44 there is only two derivations left change (external/internal) and address_index and both are not hardened or not requires xprivate key.

from hdwallet import BIP44HDWallet
from hdwallet.cryptocurrencies import TronMainnet
from hdwallet import HDWallet as HDWallet
from hdwallet.symbols import TRX as SYMBOL
import json

hdwallet: BIP44HDWallet = BIP44HDWallet(cryptocurrency=TronMainnet)
hdwallet.from_mnemonic(
    mnemonic='imitate minute bulk rapid eager captain case envelope submit bike birth unfold', language="english", passphrase=None
)
hdwallet.clean_derivation()
hdwallet.from_index(44, hardened=True)  # purpose
hdwallet.from_index(195, hardened=True)  # coin_type
hdwallet.from_index(0, hardened=True)  # account

xpublic_key=hdwallet.xpublic_key()

hdwallet: HDWallet = HDWallet(symbol=SYMBOL)
hdwallet.from_xpublic_key(xpublic_key=xpublic_key)
hdwallet.clean_derivation()

hdwallet.from_index(0)  # change (external/internal)
hdwallet.from_index(0)  # address_index

print(json.dumps(hdwallet.dumps(), indent=4, ensure_ascii=False))