1

I have created a new Electrum wallet, and here is an address:

1JkZLnmFfpVFLT2ZMtKzc6BuXMdmY41EHA

By right-clicking on it, I choose "Private key" which gives:

Kzuucz58MiTbbedeVuqBaPYwG1TQrV3n2NYU2dJRZ7HEHnHsUXWx

Now I want to be able (to learn how it works) to go from this private key to the address, via elliptic curve multiplication.

Here is what I tried:

import bitcoin #pybitcointools
import base58
import binascii

pvt = 'Kzuucz58MiTbbedeVuqBaPYwG1TQrV3n2NYU2dJRZ7HEHnHsUXWx'
pvtdecoded = base58.b58decode(pvt)
pvthex = binascii.hexlify(pvtdecoded)[2:-8]     # remove the first initial byte for version and 4 final bytes for checksum
pvt2 = bitcoin.decode_privkey(pvthex, 'hex')    # decode as a decimal

# generate pubkey from pvtkey with elliptic curve multiplication
public_key = bitcoin.fast_multiply(bitcoin.G, pvt2)
addr = bitcoin.pubkey_to_address(public_key)
print addr

which gives: 1LNSuE4NKHTyHygeKwnU1equ7MjPMhayxB which is not the original address.

What's wrong? How to recover the original address (1JkZLnmFfpVFLT2ZMtKzc6BuXMdmY41EHA) from the Private key by using elliptic curve multiplication?


Edit: As Kz....Wx private key looks like a WIF-compressed one, I tried to replace:

pvt2 = bitcoin.decode_privkey(pvthex, 'hex')    # decode as a decimal

by

pvt2 = bitcoin.decode_privkey('Kzuucz58MiTbbedeVuqBaPYwG1TQrV3n2NYU2dJRZ7HEHnHsUXWx', 'wif')

but then after elliptic curve multiplication, it gives another address, which is still not the good one! (18dFF3EQoPxR44TygdGxHPMe3LSLFeQe4U)

2

In both cases, you are not generating the address that corresponds to the compressed public key.

In the first case, you are creating what looks to be an invalid address. At the very least, the private key it is using is incorrect because it has the compression byte. This will change the value that you get for the private key when it is decoded. In order to get the same outcome as the second case, you would need to drop an additional byte from the decoded WIF key as that byte specifies compression. The key you are using there is 33 bytes rather than the 32 bytes for actual private keys.

In the second case, you are not creating the compressed public key to generate the address. It is still using the uncompressed public key. Instead of using fast_multiply and creating the public key yourself, you should be using privkey_to_pubkey. fast_multiply is a mathematical operation, it has no concept of compressed public keys. However privkey_to_pubkey does so it will create the proper public key. Then you can use that public key to generate the address.

Instead of doing

public_key = bitcoin.fast_multiply(bitcoin.G, pvt2)

you should be doing

public_key = bitcoin.privkey_to_pubkey(pvt)
  • Thanks for your helpful answer. About your last paragraph, why can't I use an elliptic curve multiplication (there should be one under the hood, even in the compressed case, right?)? Also could you just post (for future reference, and to be sure) the line of code we should use instead of fast_multiply? – Basj Jan 1 '18 at 19:11
  • Because compression occurs independently of the elliptic curve operations. Elliptic curve operations don't know what point compression is; they can only operate on the full curve. However libraries can use point compression, and that must happen separately from the EC operations. – Andrew Chow Jan 1 '18 at 19:19
  • Thanks @AndrewChow. I tried with public_key = bitcoin.privkey_to_pubkey(pvt2) but I still get 18dFF3EQoPxR44TygdGxHPMe3LSLFeQe4U instead of the original address 1JkZLnmFfpVFLT2ZMtKzc6BuXMdmY41EHA. What can be wrong? – Basj Jan 1 '18 at 19:23
  • Looking at the implementation of this pybitcointools, you don't need to decode the private key when you use privkey_to_pubkey. Rather it is decoded for you by that function itself. I will update my answer with the correct code. – Andrew Chow Jan 1 '18 at 19:27
  • Thanks a lot! I'm struggling to get 1JkZLnmFfpVFLT2ZMtKzc6BuXMdmY41EHA as output :) – Basj Jan 1 '18 at 19:29
0

Based on the accepted answer's suggestion to use privkey_to_pubkey, I analyzed its code, and so this works as well:

pvt = 'Kzuucz58MiTbbedeVuqBaPYwG1TQrV3n2NYU2dJRZ7HEHnHsUXWx'
pvtdecoded = base58.b58decode(pvt)
pvthex = binascii.hexlify(pvtdecoded)[2:-10]     # remove the first initial byte for version and 4 final bytes for checksum
pvt2 = bitcoin.decode_privkey(pvthex, 'hex')     # decode as a decimal

# generate pubkey from pvtkey with elliptic curve multiplication
public_key = bitcoin.fast_multiply(bitcoin.G, pvt2)
public_key = bitcoin.encode_pubkey(public_key, 'hex_compressed')
addr = bitcoin.pubkey_to_address(public_key)
print addr    # 1JkZLnmFfpVFLT2ZMtKzc6BuXMdmY41EHA

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.