Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I get a hex-encoded key from MPK? #881

Closed
xPtrrj opened this issue May 4, 2021 · 15 comments
Closed

How do I get a hex-encoded key from MPK? #881

xPtrrj opened this issue May 4, 2021 · 15 comments

Comments

@xPtrrj
Copy link

xPtrrj commented May 4, 2021

How do I get a Hex-encoded public key from Master Public Key?

@afk11
Copy link
Member

afk11 commented Jun 8, 2021

Sorry for the delay. Are you using BIP32? Electrum has an old style 'master public key' thing, probably not relevant

Use HierarchicalKeyFactory to decode the base58 master public key, then you have a HierarchicalKey and can call deriveChild/derivePath to derive the key you want - a child HierarchicalKey. You can call getPublicKey on the child HierarchicalKey to get the PublicKeyInterface implementation, which has a getHex() method.

@xPtrrj
Copy link
Author

xPtrrj commented Nov 7, 2021

does this support testnet keys though? for example a vpub########### key

@xPtrrj
Copy link
Author

xPtrrj commented Nov 7, 2021

even tpub, tried doing $config = new GlobalPrefixConfig([
new NetworkConfig(NetworkFactory::bitcoinTestnet(), [
$pubPrefix,
])
]);

	    and using a xpub testnet key (tpub###) . im getting Prefix not configured for network

@afk11
Copy link
Member

afk11 commented Nov 8, 2021

Does this example help a bit? https://github.com/Bit-Wasp/bitcoin-php/blob/1.0/examples/zpub.parse.php

So vpub would be a testnet key, with the bytes 0x045f1cf6 - vpub 0x045f18bc - vprv per the SLIP132, which corresponds to a P2WPKH script type

I think if you based it on the example above, just edit https://github.com/Bit-Wasp/bitcoin-php/blob/1.0/examples/zpub.parse.php#L21-L26 here to use NetworkFactory::bitcoinTestnet() as the network, and $bitcoinRegistry should be new BitcoinTestnetRegistry not BitcoinRegistry

I seem to have screwed up the comments in this file, in case that throws you (referring to it as zpub, although it's vpub on testnet)
https://github.com/Bit-Wasp/bitcoin-php/blob/1.0/src/Network/Slip132/BitcoinTestnetRegistry.php

@xPtrrj
Copy link
Author

xPtrrj commented Nov 9, 2021

I get to make it work however the address that it returns is not for testnet, it returns a wallet address that starts with bc######### where it should return an address that starts with tb######### right?

@xPtrrj
Copy link
Author

xPtrrj commented Nov 9, 2021

So when i use the address from that I get an invalid address error

Edit: Actually, if i also use $fkey->derivePath("84'/0'/0'"); it returns an error that a private master key is required. My only inputs are all pubkeys. Not private keys.

So i could get an address if i dont use $fkey->derivePath("84'/0'/0'"), i could get if i use $fkey->derivePath("0/0");
If i use $fkey->derivePath("84'/0'/0'") then it looks for a private key which i dont have from inputs.

The addresses i get from $fkey->derivePath("0/0")/ $fkey->derivePath("0/1")/ etc ; when i try to send funds then bitcoin says that the address is invalid

@afk11
Copy link
Member

afk11 commented Nov 9, 2021

I get to make it work however the address that it returns is not for testnet, it returns a wallet address that starts with bc######### where it should return an address that starts with tb######### right?

That would suggest you are passing the mainnet network, not testnet. (NetworkFactory::bitcoin() vs NetworkFactory::bitcoinTestnet())

Edit: Actually, if i also use $fkey->derivePath("84'/0'/0'"); it returns an error that a private master key is required. My only inputs are all pubkeys. Not private keys.

This is a feature of BIP32 hardened derivations. You should probably do a refresher on BIP32. Essentially if you have a public key, you cannot derive any hardened child subkeys like you are doing here (See the apostrophe in the bip32 path 84'/0'/0').

Instead of giving the root public key M, pre-derive M/84'/0'/0' and use that key in your code instead - that way all the hardened derivations are already done. You'll need the root private key to accomplish this step.

The examples are kind of sucky in that, while they illustrate one way to accomplish something, they often need rewriting to fit in with what you are trying to do.

I find deriving and comparing to bip32.org can help confirm if things are going correctly.

@xPtrrj
Copy link
Author

xPtrrj commented Nov 9, 2021

That would suggest you are passing the mainnet network, not testnet. (NetworkFactory::bitcoin() vs NetworkFactory::bitcoinTestnet())

I did so changed as instructed
$btc = NetworkFactory::bitcoinTestnet();
$bitcoinPrefixes = new BitcoinTestnetRegistry();

still gives address on mainnet;

@xPtrrj
Copy link
Author

xPtrrj commented Nov 9, 2021

This is a feature of BIP32 hardened derivations. You should probably do a refresher on BIP32. Essentially if you have a public key, you cannot derive any hardened child subkeys like you are doing here (See the apostrophe in the bip32 path 84'/0'/0').

Instead of giving the root public key M, pre-derive M/84'/0'/0' and use that key in your code instead - that way all the hardened derivations are already done. You'll need the root private key to accomplish this step.

The examples are kind of sucky in that, while they illustrate one way to accomplish something, they often need rewriting to fit in with what you are trying to do.

I find deriving and comparing to bip32.org can help confirm if things are going correctly.

Is there an example of doing this?

@xPtrrj
Copy link
Author

xPtrrj commented Nov 10, 2021

@afk11
Copy link
Member

afk11 commented Nov 10, 2021

Oh, I think I understand the address network problem - can you pass that testnet network into the final getAddress call?

    return $child_key->getAddress(new AddressCreator())->getAddress();

becomes

    return $child_key->getAddress(new AddressCreator())->getAddress($btc);

The objects in this library tend not to store which network they belong to, so calling AddressInterface::getAddress() without passing a custom network will use the 'default network' (whatever `Bitcoin::getNetwork()`` returns)

I'm kicking myself I didn't catch it sooner :)

The rule when dealing with multiple networks in your codebase - whenever you call something that outputs a base58/bech32 etc encoding, always pass the network you want it for.

I think there's a PR open for a network to make the default network issue much easier to detect. See #716

@afk11
Copy link
Member

afk11 commented Nov 10, 2021

This is a feature of BIP32 hardened derivations. You should probably do a refresher on BIP32. Essentially if you have a public key, you cannot derive any hardened child subkeys like you are doing here (See the apostrophe in the bip32 path 84'/0'/0').
Instead of giving the root public key M, pre-derive M/84'/0'/0' and use that key in your code instead - that way all the hardened derivations are already done. You'll need the root private key to accomplish this step.
The examples are kind of sucky in that, while they illustrate one way to accomplish something, they often need rewriting to fit in with what you are trying to do.
I find deriving and comparing to bip32.org can help confirm if things are going correctly.

Is there an example of doing this?

No. Write a separate script, taking a root private key, and deriving 84'/0'/0'. then convert to the public key (path: M/84'/0'/0') and printing out the new encoded key. Then copy that public key into your app instead of the key you are using - which I am guessing is the root public key (path: M)

Don't forget you can test with bip32.org

@xPtrrj
Copy link
Author

xPtrrj commented Nov 10, 2021

No. Write a separate script, taking a root private key, and deriving 84'/0'/0'. then convert to the public key (path: M/84'/0'/0') and printing out the new encoded key. Then copy that public key into your app instead of the key you are using - which I am guessing is the root public key (path: M)

I understand this however the problem with this is that I or the users wont put in their private keys into the system as it'll give them risks. I dont understand what you mean by root public key, on my test im using Electrum wallet, Then Wallet>information>master public key

@xPtrrj
Copy link
Author

xPtrrj commented Nov 11, 2021

Oh, I think I understand the address network problem - can you pass that testnet network into the final getAddress call?

    return $child_key->getAddress(new AddressCreator())->getAddress();

becomes

    return $child_key->getAddress(new AddressCreator())->getAddress($btc);

The objects in this library tend not to store which network they belong to, so calling AddressInterface::getAddress() without passing a custom network will use the 'default network' (whatever `Bitcoin::getNetwork()`` returns)

I'm kicking myself I didn't catch it sooner :)

The rule when dealing with multiple networks in your codebase - whenever you call something that outputs a base58/bech32 etc encoding, always pass the network you want it for.

I think there's a PR open for a network to make the default network issue much easier to detect. See #716

This is a feature of BIP32 hardened derivations. You should probably do a refresher on BIP32. Essentially if you have a public key, you cannot derive any hardened child subkeys like you are doing here (See the apostrophe in the bip32 path 84'/0'/0').
Instead of giving the root public key M, pre-derive M/84'/0'/0' and use that key in your code instead - that way all the hardened derivations are already done. You'll need the root private key to accomplish this step.
The examples are kind of sucky in that, while they illustrate one way to accomplish something, they often need rewriting to fit in with what you are trying to do.
I find deriving and comparing to bip32.org can help confirm if things are going correctly.

Is there an example of doing this?

No. Write a separate script, taking a root private key, and deriving 84'/0'/0'. then convert to the public key (path: M/84'/0'/0') and printing out the new encoded key. Then copy that public key into your app instead of the key you are using - which I am guessing is the root public key (path: M)

Don't forget you can test with bip32.org

Problem is solved;

@afk11
Copy link
Member

afk11 commented Nov 16, 2021

dont understand what you mean by root public key, on my test im using Electrum wallet, Then Wallet>information>master public key

Ah apologies, now I understand

Problem is solved;

Great to hear

@afk11 afk11 closed this as completed Nov 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants