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)
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:
No comments yet.
Write a comment