Can We Turn a Nostr Public Key Into a Non-Taproot Bitcoin Address In Prod?

Yes, I've been consistently getting the same keypairs using my proposed rule for deterministic legacy address derivation from x-only public keys (npub) & nsec-to-WIF importing in Electrum (no prefix for p2pkh import & prefix "p2wpkh:" for that type)
Can We Turn a Nostr Public Key Into a Non-Taproot Bitcoin Address In Prod?

Does this qualify as my whitepaper?

Anyway, here’s how I do it in prod…

import hashlib
import base58

from bech32 import (
    bech32_decode,
    bech32_encode,
    convertbits,
)

from Crypto.Hash import RIPEMD160


# -----------------------------
# Nostr npub -> x-only pubkey
# -----------------------------

def decode_npub(npub: str) -> bytes:
    hrp, data = bech32_decode(npub)

    if hrp != "npub":
        raise ValueError("Invalid npub")

    decoded = bytes(convertbits(data, 5, 8, False))

    if len(decoded) != 32:
        raise ValueError("Expected 32-byte pubkey")

    return decoded


# -----------------------------
# HASH160
# -----------------------------

def hash160(data: bytes) -> bytes:
    sha = hashlib.sha256(data).digest()

    ripemd = RIPEMD160.new()
    ripemd.update(sha)

    return ripemd.digest()


# -----------------------------
# P2PKH
# -----------------------------

def pubkey_to_p2pkh(pubkey: bytes) -> str:
    h160 = hash160(pubkey)

    payload = b"\x00" + h160

    checksum = hashlib.sha256(
        hashlib.sha256(payload).digest()
    ).digest()[:4]

    return base58.b58encode(payload + checksum).decode()


# -----------------------------
# P2WPKH
# -----------------------------

def pubkey_to_p2wpkh(pubkey: bytes) -> str:
    h160 = hash160(pubkey)

    data = [0] + convertbits(h160, 8, 5)

    return bech32_encode("bc", data)


# -----------------------------
# Main conversion
# -----------------------------

def npub_to_addresses(npub: str):
    xonly = decode_npub(npub)

    # Assume even-y parity
    compressed_pubkey = b"\x02" + xonly

    return {
        "compressed_pubkey_hex": compressed_pubkey.hex(),
        "p2pkh": pubkey_to_p2pkh(compressed_pubkey),
        "p2wpkh": pubkey_to_p2wpkh(compressed_pubkey),
    }


# -----------------------------
# Example
# -----------------------------

if __name__ == "__main__":

    npub = "YOURNPUBHERE"

    result = npub_to_addresses(npub)

    print("Compressed pubkey:")
    print(result["compressed_pubkey_hex"])

    print("\nP2PKH:")
    print(result["p2pkh"])

    print("\nP2WPKH:")
    print(result["p2wpkh"])

&

import base58
import hashlib
from bech32 import bech32_decode, convertbits

def nsec_to_wif(nsec_str):
    # 1. Decode nsec (Bech32) to 32-byte hex
    hrp, data5 = bech32_decode(nsec_str)
    if hrp != 'nsec':
        raise ValueError("Invalid nsec string")
    
    decoded = convertbits(data5, 5, 8, False)
    private_key_hex = bytes(decoded).hex()
    
    # 2. Add mainnet byte (80) and compression byte (01)
    extended_key = '80' + private_key_hex + '01'
    
    # 3. Double SHA-256 checksum
    first_sha = hashlib.sha256(bytes.fromhex(extended_key)).digest()
    second_sha = hashlib.sha256(first_sha).digest()
    checksum = second_sha.hex()[:8]
    
    # 4. Base58 encoding
    final_key_hex = extended_key + checksum
    wif = base58.b58encode(bytes.fromhex(final_key_hex)).decode('utf-8')
    
    return wif

my_nsec = "YOURNSECHERE"

bitcoin_wif = nsec_to_wif(my_nsec)
print("Your Bitcoin WIF Key is:", bitcoin_wif)

P.S. This is possible with cryptography, but you should only keep watch on these addresses without making them public knowledge & know how to spend them to 0 - if someone actually sends you BTC after deriving on their own/via some website’s tool.

If you want to give out public addresses derived from your nsec, hardened by passphrase salting, see why & how, here:

https://primal.net/a/naddr1qvzqqqr4gupzqtshhpkkjtyhnw6m0skah8msuqh8dw7rtzh6jkkxsy6sg7a7zrszqpzxsmmh946x7tt5w4exuttp94hx7um5wgkkuum9vvkkjmn5dukkzttgv9exgetwv4jz6mn0dckhgctswfhk7apdv9jxgun9wdej66tw94c8ymmy46xjya


Write a comment
No comments yet.