Skip to content

Instantly share code, notes, and snippets.

@Bobalot
Last active January 3, 2016 09:59
Show Gist options
  • Save Bobalot/8446822 to your computer and use it in GitHub Desktop.
Save Bobalot/8446822 to your computer and use it in GitHub Desktop.
Bitcoin stealth address example without needing to store data in the blockchain. Payee can listen for new addresses against the shared secret S, as long as they know the master public key of who is sending the payment(s)
# Example of stealth addressess using Diffie-Hellman, to create a unique offset between 2 mpks
# doesn't need to exchange data in the blockchain. Payee can just look for new transactions against their
# mpk at offset S, where S is the shared secret between the two parties.
# This will require the payee to subscribe to n addresses,
# where n is the number of identities they expect to receive a payment from.
import obelisk
import os
from ecdsa import numbertheory, curves, util, ellipticcurve
# From https://github.com/FelixWeis/python-hdwallet/blob/master/hdwallet/hdwallet.py
def point_decompress(data):
prefix = data[0]
curve = curves.SECP256k1.curve
if prefix == '\x04':
return data
assert(prefix in ['\x02', '\x03', '\x04'])
parity = 1 if prefix == '\x02' else -1
x = util.string_to_number(data[1:])
y = numbertheory.square_root_mod_prime(
( x * x * x + curve.a() * x + curve.b() ) % curve.p(), curve.p()
)
y = parity * y % curve.p()
return ellipticcurve.Point(curve, x, y)
def diffie_hellman(e, Q):
point = point_decompress(Q)
e_int = obelisk.string_to_number(e)
point = e_int * point
# convert x point to bytes
result = "\x03" + ("%x" % point.x()).decode("hex")
assert len(result) == 33
return result
def test_stealth(bob_seed, alice_seed):
# Sequence could be anything, ideally it's common and well known, like the root.
# User could use their branches to create alternate identities,
# but they'd then have to remember the sequence along with the seed
bob_wallet = obelisk.HighDefWallet.root(bob_seed)
alice_wallet = obelisk.HighDefWallet.root(alice_seed)
bob_secret = bob_wallet.secret
alice_secret = alice_wallet.secret
bob_mpk = bob_wallet.mpk_compressed
alice_mpk = alice_wallet.mpk_compressed
print "Bob secret", bob_secret.encode("hex")
print "Alice secret", alice_secret.encode("hex")
print "\n"
print "Bob mpk (uncompressed)", bob_wallet.mpk.encode("hex")
print "Alice mpk (uncompressed)", alice_wallet.mpk.encode("hex")
print "\n"
print "Bob mpk (compressed)", bob_mpk.encode("hex")
print "Alice mpk (compressed)", alice_mpk.encode("hex")
print "\n"
# Trade mpks now over the internet/telegram whatever.
# Calculate shared secret S.
# Bob S = bob_secret * alice_mpk
# Alice S = alice_secret * bob_mpk
bob_shared_secret = diffie_hellman(bob_secret, alice_mpk)
alice_shared_secret = diffie_hellman(alice_secret, bob_mpk)
assert(bob_shared_secret == alice_shared_secret)
print "Bob Shared secret", bob_shared_secret.encode("hex")
print "Alice Shared secret", alice_shared_secret.encode("hex")
print "\n"
# Since the secret is unique to both we can use it as the initial offset and then use a value n
# for every subsequent payment to each person to maintain anonymity, or breaking another chain from the hd wallet
# Chop the 0x03 off the beginning.
secret_int = long(bob_shared_secret[1:].encode("hex"), base=16)
bob_sequence_to_alice = obelisk.BIP32Sequence(point_decompress(alice_mpk))
alice_sequence_to_bob = obelisk.BIP32Sequence(point_decompress(bob_mpk))
print "Shared secret", secret_int
print "\n"
# Need to add generation from mpk, so we can check bob_receieve(i) == alice_send(i)
# and vice versa.
for i in range(10):
bob_receive = bob_wallet.branch(secret_int + i).address
print "bob_receive", bob_receive
print "===================="
for i in range(10):
alice_receive = alice_wallet.branch(secret_int + i).address
print "alice_receive", alice_receive
def exhaustive_test(iterations=50):
for i in range(iterations):
print "\n\nIteration", i
bob_seed = os.urandom(16).encode("hex")
alice_seed = os.urandom(16).encode("hex")
test_stealth(bob_seed, alice_seed)
if __name__ == "__main__":
bob_seed = "000102030405060708090a0b0c0d0e0f"
alice_seed = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"
al = test_stealth(bob_seed, alice_seed)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment