Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ PRIVKEY=e126f68f7eafcc8b74f54d269fe206be715000f94dac067d1c04a8ca3b2db734
PUBKEY=03e7156ae33b0a208d0744199163177e909e80176e55d97a2f221ede0f934dd9ad
# Reproducable example, set timestamp.
TIMESTAMP=--timestamp=1496314658

# Secret hash for the example.
SECRETE='1111111111111111111111111111111111111111111111111111111111111111'
LONG_DESCRIPTION='One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon'

to_btc()
Expand All @@ -17,33 +18,36 @@ to_btc()
}

echo "### Please make a donation of any amount using rhash $RHASH to me @$PUBKEY"
./lightning-address.py encode $TIMESTAMP --no-amount 0 --description='Please consider supporting this project' $RHASH $PRIVKEY
./lightning-address.py encode $TIMESTAMP --no-amount 0 --description='Please consider supporting this project' $RHASH $PRIVKEY $SECRETE
echo

echo "### Please send \$3 for a cup of nonsense (ナンセンス 1杯) to the same peer, within 1 minute"
./lightning-address.py encode $TIMESTAMP --description='ナンセンス 1杯' $(to_btc 3) --expires=60 $RHASH $PRIVKEY
./lightning-address.py encode $TIMESTAMP --description='ナンセンス 1杯' $(to_btc 3) --expires=60 $RHASH $PRIVKEY $SECRETE
echo

echo "### Now send \$24 for an entire list of things (hashed)"
./lightning-address.py encode $TIMESTAMP --description-hashed="$LONG_DESCRIPTION" $(to_btc 24) $RHASH $PRIVKEY
./lightning-address.py encode $TIMESTAMP --description-hashed="$LONG_DESCRIPTION" $(to_btc 24) $RHASH $PRIVKEY $SECRETE
echo

echo '### The same, on testnet, with a fallback address mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP'
./lightning-address.py encode $TIMESTAMP --currency=tb --fallback=mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP --description-hashed="$LONG_DESCRIPTION" $(to_btc 24) $RHASH $PRIVKEY
./lightning-address.py encode $TIMESTAMP --currency=tb --fallback=mk2QpYatsKicvFVuTAQLBryyccRXMUaGHP --description-hashed="$LONG_DESCRIPTION" $(to_btc 24) $RHASH $PRIVKEY $SECRETE
echo

echo '### On mainnet, with fallback address 1RustyRX2oai4EYYDpQGWvEL62BBGqN9T with extra routing info to go via nodes 029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255 then 039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255'
./lightning-address.py encode $TIMESTAMP --route=029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255/0102030405060708/1/20/3/039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255/030405060708090a/2/30/4 --fallback=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T --description-hashed="$LONG_DESCRIPTION" $(to_btc 24) $RHASH $PRIVKEY
./lightning-address.py encode $TIMESTAMP --route=029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255/0102030405060708/1/20/3/039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255/030405060708090a/2/30/4 --fallback=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T --description-hashed="$LONG_DESCRIPTION" $(to_btc 24) $RHASH $PRIVKEY $SECRETE
echo

echo '### On mainnet, with fallback (p2sh) address 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX'
./lightning-address.py encode $TIMESTAMP --description-hashed="$LONG_DESCRIPTION" --fallback=3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX $(to_btc 24) $RHASH $PRIVKEY
./lightning-address.py encode $TIMESTAMP --description-hashed="$LONG_DESCRIPTION" --fallback=3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX $(to_btc 24) $RHASH $PRIVKEY $SECRETE
echo

echo '### On mainnet, with fallback (p2wpkh) address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
./lightning-address.py encode $TIMESTAMP --description-hashed="$LONG_DESCRIPTION" --fallback=bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 $(to_btc 24) $RHASH $PRIVKEY
./lightning-address.py encode $TIMESTAMP --description-hashed="$LONG_DESCRIPTION" --fallback=bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 $(to_btc 24) $RHASH $PRIVKEY $SECRETE
echo

echo '### On mainnet, with fallback (p2wsh) address bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3'
./lightning-address.py encode $TIMESTAMP --description-hashed="$LONG_DESCRIPTION" --fallback=bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3 $(to_btc 24) $RHASH $PRIVKEY
echo '### On mainnet, with fallback (p2wpkh) address bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4' features='100000100000000'
./lightning-address.py encode --timestamp=1496314658 --description="One piece of chocolate cake" --fallback=bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 --features='100000100000000' $(to_btc 24) $RHASH $PRIVKEY $SECRETE
echo

echo '### On mainnet, with fallback (p2wsh) address bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3'
./lightning-address.py encode $TIMESTAMP --description-hashed="$LONG_DESCRIPTION" --fallback=bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3 $(to_btc 24) $RHASH $PRIVKEY $SECRETE
16 changes: 15 additions & 1 deletion lightning-address.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def encode(options):
if options.fallback:
addr.tags.append(('f', options.fallback))

if options.features:
addr.tags.append(('9', int(options.features, 2)))

addr.tags.append(('s', unhexlify(options.secret)))

for r in options.route:
splits = r.split('/')
route=[]
Expand Down Expand Up @@ -78,7 +83,14 @@ def tags_by_name(name, tags):
if expiry:
print("Expiry (seconds):", expiry[0])

for t in [t for t in a.tags if t[0] not in 'rdfhx']:
secret = tags_by_name('s', a.tags)
if secret:
print("Secret:", hexlify(secret[0]))

for f in tags_by_name('9', a.tags):
print("Features: {}".format(bin(f).zfill(8)))

for t in [t for t in a.tags if t[0] not in 'rdfhxs9']:
print("UNKNOWN TAG {}: {}".format(t[0], hexlify(t[1])))

parser = argparse.ArgumentParser(description='Encode lightning address')
Expand All @@ -104,9 +116,11 @@ def tags_by_name(name, tags):
help='Timestamp (seconds after epoch) instead of now')
parser_enc.add_argument('--no-amount', action="store_true",
help="Don't encode amount")
parser_enc.add_argument('--features', help='Features to encode in binary string (e.g., 10000010)')
parser_enc.add_argument('amount', type=float, help='Amount in currency')
parser_enc.add_argument('paymenthash', help='Payment hash (in hex)')
parser_enc.add_argument('privkey', help='Private key (in hex)')
parser_enc.add_argument('secret', help='payment address (payment secret in hex)')
parser_enc.set_defaults(func=encode)

parser_dec.add_argument('lnaddress', help='Address to decode')
Expand Down
24 changes: 22 additions & 2 deletions lnaddr.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,8 @@ def lnencode(addr, privkey):

# BOLT #11:
#
# A writer MUST NOT include more than one `d`, `h`, `n` or `x` fields,
if k in ('d', 'h', 'n', 'x'):
# A writer MUST NOT include more than one `s`, `d`, `h`, `n` or `x` fields,
if k in ('s', 'd', 'h', 'n', 'x'):
if k in tags_set:
raise ValueError("Duplicate '{}' tag".format(k))

Expand All @@ -206,6 +206,14 @@ def lnencode(addr, privkey):
data += tagged_bytes('h', hashlib.sha256(v.encode('utf-8')).digest())
elif k == 'n':
data += tagged_bytes('n', v)
elif k == 's':
data += tagged_bytes('s', v)
elif k == '9':
if v == 0:
continue
# Calcular quantos bits são necessários para representar 'v'
bits_needed = v.bit_length() or 1
data += tagged('9', bitstring.pack(f'uint:{bits_needed}', v))
else:
# FIXME: Support unknown tags?
raise ValueError("Unknown tag {}".format(k))
Expand Down Expand Up @@ -240,6 +248,8 @@ def __init__(self, paymenthash=None, amount=None, currency='bc', tags=None, date
self.pubkey = None
self.currency = currency
self.amount = amount
self.features = None
self.secret = None

def __str__(self):
return "LnAddr[{}, amount={}{} tags=[{}]]".format(
Expand Down Expand Up @@ -347,6 +357,16 @@ def lndecode(a, verbose=False):
continue
addr.pubkey = secp256k1.PublicKey(flags=secp256k1.ALL_FLAGS)
addr.pubkey.deserialize(trim_to_bytes(tagdata))
elif tag == 's':
if data_length != 52:
addr.unknown_tags.append((tag, tagdata))
continue
addr.tags.append(('s', trim_to_bytes(tagdata)))
elif tag == '9':
feature = tagdata.uint
if feature == 0:
continue
addr.tags.append((tag, feature))
else:
addr.unknown_tags.append((tag, tagdata))

Expand Down