create account

Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python by brianoflondon

View this thread on: hive.blogpeakd.comecency.com
· @brianoflondon · (edited)
$47.91
Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python
<div class="pull-right">


![lightning-1625550_1920.jpg](https://files.peakd.com/file/peakd-hive/brianoflondon/Eo1wSwZBvrJ8mKxwoVg99dFMDosnnaYRmsoiLiQJVaU5G1jgSNppSY3CFtJ7KH5Y97V.jpg)
Image by <a href="https://pixabay.com/users/ronberg-3029970/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1625550" class="keychainify-checked">Ron van den Berg</a> from <a href="https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1625550" class="keychainify-checked">Pixabay</a>

</div>

It took me 3 specific tries over 6 months to figure this out. It could have been easy but Lightning's docs are not what they should be and I just couldn't find any solid code out on the net that actually does what I was trying to do.

Jump to the end if you want to actually see Python code.

This wasn't a core part of what I'm building, mostly I needed it to TEST what I've built. The Podcasting 2.0 Value 4 Value system is built on something called Keysend Payments within Lightning. These were a bolt on cludge that was added in the last year and a half.

They are vital because they are the only thing which allows UNSOLICITED funds to be sent via the Lightning network.

## Normal Lightning

The normal way of operating in Lightning (and this is counter to all your regular experience with other crypto payment systems) is this:

1. Receiver's Node Creates an Invoice (with a unique key) -> 
2. Receiver's Node sends or shows invoice (QR code) -> 
3. Payer sees and pays invoice ->
4. Payer's node tries to find a path to send sats via channels to the Receiver's node ->
5. Path is found and time locked agreements made with all the channels in the path to transfer on an encrypted payment (sometimes taking fees along the way) ->
6. Payment is sent and the Receiver unwraps it to verify that keys and hashes match and sats (payment) has moved to the Receiver's node in the channel it came in on. I've seen payments I send take 5 hops to get to a destination.

And we're done. But that involves generating an invoice. In fact steps 1 & 2 are what happens when you [click on a link to send someone Lightning and get shown a QR Code like this link](https://lnd.v4v.app/hive/get/HIVE/@brianoflondon/3333/Thank%20you%20for%20clicking%20on%20the%20link!%20Send%20me%203333%20sats%20as%20Hive%20if%20you%20want!)

## Keysend is different

The difference with how Keysend works for us in streaming sats payments is that steps 1 to 3 don't happen.

Instead, the Payer's node comes up with a secret on its own. It wraps that up in a special payload section and then tries to find a path to the receiver's node.

1. Payer's node creates a secret token
2. Payer's node tries to find a payment path to the Receiver's node address
3. If that path is found it sends the payment with a the secret hidden inside and a SHA256 Hash of the secret visible to all the nodes passing the payment.
4. The Receiver's node gets the payment and can verify the hash and the secret match.

The practical upshot of this is that Lightning the payment system does actually resemble lightning the physical phenomena. ]Lightning strikes do feature a path of ionized air being formed milliseconds before the main bolt of electricity moves from the ground up](https://www.youtube.com/watch?v=qQKhIK4pvYo).

And I can see this when I receive payments: this is the log of watching for new invoices to come in when I get a Keysend payment:

```log
2022-03-24T07:49:05+0000 : New invoice received: Value: 24 | Timer: 265.371702
2022-03-24T07:49:05+0000 : Invoice has no htlcs:           | Timer: 265.372052
2022-03-24T07:49:05+0000 : New invoice received: Value: 24 | Timer: 265.373830
```

As you can see, the same invoices is registered twice, my code ignores the first one (which is missing 'htlcs') because only when the second notification occurs does the useful information come in. This bit tells me which podcast was being listened to and which Hive address I should pass the payment on to.

As you can see 0.002s passes between these two points.

Mostly this is just an artefact of the way the LND software (which I'm using for my Lightning node) operates, but I thought it was interesting.

## Python

I first got some help in this [discussion thread on Github](https://github.com/lightningnetwork/lnd/discussions/6357#discussioncomment-2424350) and I've now submitted the following text to the official documentation. I'm not sure they'll like my chatty style.

### Sending a Keysend Payment in Python with `lnd`'s REST API endpoint

This document is born out of some personal frustration: I found it particularlly hard to find working examples of using the LND Rest API (specifically on an Umbrel) from Python.

I will present here some working code examples to send a Keysend payment from Python. At first you'd think this was trivial considering how easy it is with `lncli`:

`lncli sendpayment -d 0266ad2656c7a19a219d37e82b280046660f4d7f3ae0c00b64a1629de4ea567668 -a 1948 --keysend --data 818818=627269616e6f666c6f6e646f6e --json`

That will send 1948 sats to the public key `0266ad2656c7a19a219d37e82b280046660f4d7f3ae0c00b64a1629de4ea567668` and add a special key of `818818` which passes the Hive address of `brianoflondon` as Hex: `627269616e6f666c6f6e646f6e`

To find this Hex value in Python:
```
>>> a = "brianoflondon"
>>> a.encode().hex()
'627269616e6f666c6f6e646f6e'
```

## How to do that with Python:

Actually getting this working and figuring out all the correct combinations of base64 and Hex encoding had me tearing my hair out. When it worked after I think I tried almost every possible combingation of `.encode()` `hex()` and `base64.b64encode(plain_str.encode()).decode()` left me feeling like the head of a team of monkeys which had just finished the last page typing out The Complete Works of William Shakespear.

I'm going to assume you've successfully managed to get a connection up and running to your LND API. If you haven't perhaps that needs to be better explained somewhere in these docs. Perhaps I can be persuaded.

I've documented this code and whilst its a bit different from what I'm actually using (I'm working on streaming value 4 value payments in podcasting so I'm sending quite a bit more information encoded in the `dest_custom_records` field but the method is exactly the same as I've shown here for the `818818` field). [You can learn more about these Podcasting specific fields here](https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md).

```python

import base64
import codecs
import json
import os
from hashlib import sha256
from secrets import token_hex
from typing import Tuple

import httpx



def get_lnd_headers_cert(
    admin: bool = False, local: bool = False, node: str = None
) -> Tuple[dict, str]:
    """Return the headers and certificate for connecting, if macaroon passed as string
    does not return a certificate (Voltage)"""
    if not node:
        node = Config.LOCAL_LND_NODE_ADDRESS

    # maintain option to work with local macaroon and umbrel
    macaroon_folder = ".macaroon"
    if not admin:
        macaroon_file = "invoices.macaroon"
    else:
        macaroon_file = "admin.macaroon"
    macaroon_path = os.path.join(macaroon_folder, macaroon_file)
    cert = os.path.join(macaroon_folder, "tls.cert")
    macaroon = codecs.encode(open(macaroon_path, "rb").read(), "hex")
    headers = {"Grpc-Metadata-macaroon": macaroon}
    return headers, cert


def b64_hex_transform(plain_str: str) -> str:
    """Returns the b64 transformed version of a hex string"""
    a_string = bytes.fromhex(plain_str)
    return base64.b64encode(a_string).decode()


def b64_transform(plain_str: str) -> str:
    """Returns the b64 transformed version of a string"""
    return base64.b64encode(plain_str.encode()).decode()


def send_keysend(
    amt: int,
    dest_pubkey: str = "",
    hive_accname: str = "brianoflondon",
) -> dict:
    """Pay a keysend invoice using the chosen node"""
    node = "https://umbrel.local:8080/"
    headers, cert = get_lnd_headers_cert(admin=True, node=node)
    if not dest_pubkey:
        dest_pubkey = my_voltage_public_key

    # Base 64 encoded destination bytes
    dest = b64_hex_transform(dest_pubkey)

    # We generate a random 32 byte Hex pre_image here.
    pre_image = token_hex(32)
    # This is the hash of the pre-image
    payment_hash = sha256(bytes.fromhex(pre_image))

    # The record 5482373484 is special: it carries the pre_image
    # to the destination so it can be compared with the hash we
    # pass via the payment_hash
    dest_custom_records = {
        5482373484: b64_hex_transform(pre_image),
        818818: b64_transform(hive_accname),
    }

    url = f"{node}v1/channels/transactions"
    data = {
        "dest": dest,
        "amt": amt,
        "payment_hash": b64_hex_transform(payment_hash.hexdigest()),
        "dest_custom_records": dest_custom_records,
    }

    response = httpx.post(
        url=url, headers=headers, data=json.dumps(data), verify=cert
        )

    print(json.dumps(response.json(), indent=2))
    return json.dumps(response.json(), indent=2)

```


This explanation of what is going on here is pretty useful too:

>Since you're not paying an invoice, the receiver doesn't know the preimage for the given payment's hash. You need to add the preimage to your custom records. The number is 5482373484.
>
>It also looks like you need to actually set the payment's hash (payment_hash=sha256(preimage)) of the payment.
>
>See https://github.com/lightningnetwork/lnd/blob/master/cmd/lncli/cmd_payments.go#L358 on how it's done on the RPC level (what's behind the --keysend flag of lndcli sendpayment).

If you still have question, find me @brianoflondon pretty much anywhere online and I'll help if I can.









-------
**[Support Proposal 201 on PeakD](https://peakd.com/me/proposals/201)
[Support Proposal 201 with Hivesigner](https://hivesigner.com/sign/update-proposal-votes?proposal_ids=%5B201%5D&approve=true)
[Support Proposal 201 on Ecency](https://ecency.com/proposals/201)**
-------

![brianoflondon hive footer.png](https://files.peakd.com/file/peakd-hive/brianoflondon/fIN9elzs-brianoflondon20hive20footer.png)

- [Vote for APSHamilton's Witness KeyChain or HiveSigner](https://vote.hive.uno/@apshamilton)
- [Vote for APSHamilton's Witness direct with HiveSigner](https://hivesigner.com/sign/account-witness-vote?witness=apshamilton&approve=1)
- [Get Brave](https://brave.com/bri740)
- [Use my referral link for crypto.com](https://platinum.crypto.com/r/wkva2kezch) to sign up and we both get $25 USD
- [Sign up for BlockFi](https://blockfi.com/?ref=96f858b3)
- [Find my videos on 3speak](https://3speak.online/user/brianoflondon)
- [Join the JPBLiberty Class Action law suit](https://jpbliberty.formstack.com/forms/class_member_signup?Referral=brianoflondon)
	- [Verify my ID and Send me a direct message on Keybase](https://keybase.io/brianoflondon)
👍  , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , and 133 others
properties (23)
authorbrianoflondon
permlinklightning-keysend-is-strange-and-how-to-send-keysend-payment-in-lightning-with-the-lnd-rest-api-via-python
categoryhive-110369
json_metadata"{"app":"peakd/2022.03.7","format":"markdown","description":"A short explanation and then some real code","tags":["development","python","lightning","leofinance","proofofbrain","stemgeeks","podcasting2","v4vapp"],"users":["brianoflondon","apshamilton"],"image":["https://files.peakd.com/file/peakd-hive/brianoflondon/Eo1wSwZBvrJ8mKxwoVg99dFMDosnnaYRmsoiLiQJVaU5G1jgSNppSY3CFtJ7KH5Y97V.jpg","https://files.peakd.com/file/peakd-hive/brianoflondon/fIN9elzs-brianoflondon20hive20footer.png"]}"
created2022-03-24 08:46:27
last_update2022-03-24 15:59:12
depth0
children11
last_payout2022-03-31 08:46:27
cashout_time1969-12-31 23:59:59
total_payout_value24.008 HBD
curator_payout_value23.902 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length11,002
author_reputation760,626,613,375,672
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,641,304
net_rshares33,568,569,237,826
author_curate_reward""
vote details (197)
@arcange ·
#### <div class="phishy">WARNING - The message you received from @cve3 is a CONFIRMED SCAM!</div>
**DO NOT FOLLOW** any instruction and **DO NOT CLICK** on any link in the comment!
properties (22)
authorarcange
permlinknotify-brianoflondon-20220324154550
categoryhive-110369
json_metadata{"image":["https://i.imgur.com/2auxMll.png"]}
created2022-03-24 15:45:51
last_update2022-03-24 15:45:51
depth1
children0
last_payout2022-03-31 15:45:51
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length180
author_reputation1,146,616,139,479,238
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,650,931
net_rshares0
@behiver ·
Great information and tutorial and Python rules! Easy to use, does the job faster than in other languages and you have everything laid down in the post...what else could somebody want? :))

Posted Using [LeoFinance <sup>Beta</sup>](https://leofinance.io/@behiver/re-brianoflondon-q3ybq)
properties (22)
authorbehiver
permlinkre-brianoflondon-q3ybq
categoryhive-110369
json_metadata{"app":"leofinance/0.2","format":"markdown","tags":["hive-110369","leofinance","hive-167922"],"canonical_url":"https://leofinance.io/@behiver/re-brianoflondon-q3ybq"}
created2022-03-24 13:35:54
last_update2022-03-24 13:35:54
depth1
children0
last_payout2022-03-31 13:35:54
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length286
author_reputation567,890,467,975,745
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,647,308
net_rshares0
@cve3 ·
Nice keep it up.
if you haven't receive 300 HIVE and 200 LEO than vote **LeoFinance** for witness
LeoFinance is a well known and great project so i am vouching for it
Vote  for LeoFinance and get 300 HIVE and 200 LEO now
[CLICK HERE TO VOTE NOW](https://54896-78481.live/19205)
👎  , ,
properties (23)
authorcve3
permlinkr98sh8
categoryhive-110369
json_metadata{"links":["https://54896-78481.live/19205"],"app":"hiveblog/0.1"}
created2022-03-24 09:17:39
last_update2022-03-24 09:17:39
depth1
children3
last_payout2022-03-31 09:17:39
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length277
author_reputation51,667,871,453,708
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,641,968
net_rshares-2,061,957,742,150
author_curate_reward""
vote details (3)
@techlhab ·
$2.91
Pls beware of this link. It highly possible to be a scam❗❗❗❗.
👍  
properties (23)
authortechlhab
permlinkre-cve3-r98x82
categoryhive-110369
json_metadata{"tags":["hive-110369"],"app":"peakd/2022.03.7"}
created2022-03-24 11:00:12
last_update2022-03-24 11:00:12
depth2
children2
last_payout2022-03-31 11:00:12
cashout_time1969-12-31 23:59:59
total_payout_value1.453 HBD
curator_payout_value1.454 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length61
author_reputation11,066,014,147,515
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,644,040
net_rshares2,030,034,780,197
author_curate_reward""
vote details (1)
@brianoflondon ·
Thanks, I've muted that in this community.
properties (22)
authorbrianoflondon
permlinkre-techlhab-r992p8
categoryhive-110369
json_metadata{"tags":["hive-110369"],"app":"peakd/2022.03.7"}
created2022-03-24 12:58:21
last_update2022-03-24 12:58:21
depth3
children0
last_payout2022-03-31 12:58:21
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length42
author_reputation760,626,613,375,672
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,646,599
net_rshares0
@savvyplayer ·
It's already a confirmed scam. Thanks for helping the community avoid clicking that scam link. 

!PIZZA
👍  
👎  ,
properties (23)
authorsavvyplayer
permlinkre-techlhab-2022324t192220440z
categoryhive-110369
json_metadata{"tags":["hive-110369"],"app":"ecency/3.0.22-vision","format":"markdown+html"}
created2022-03-24 11:22:18
last_update2022-03-24 11:22:18
depth3
children0
last_payout2022-03-31 11:22:18
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length103
author_reputation21,811,125,531,217
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,644,560
net_rshares-16,996,456,631,832
author_curate_reward""
vote details (3)
@emeka4 ·
This is really nice and helpful which I appreciate a lot, keep up the good work moving
properties (22)
authoremeka4
permlinkr98ram
categoryhive-110369
json_metadata{"app":"hiveblog/0.1"}
created2022-03-24 08:52:06
last_update2022-03-24 08:52:06
depth1
children0
last_payout2022-03-31 08:52:06
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length86
author_reputation234,166,618,016,346
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,641,388
net_rshares0
@heskay ·
Thanks for sharing.it was a great one
properties (22)
authorheskay
permlinkre-brianoflondon-2022324t11933461z
categoryhive-110369
json_metadata{"tags":["development","python","lightning","leofinance","proofofbrain","stemgeeks","podcasting2","v4vapp"],"app":"ecency/3.0.22-vision","format":"markdown+html"}
created2022-03-24 10:11:30
last_update2022-03-24 10:11:30
depth1
children0
last_payout2022-03-31 10:11:30
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length37
author_reputation81,253,609,298,220
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,643,168
net_rshares0
@olympicdragon ·
This is great. Now you have keysend payment. It's so innovative. It only took you few months to worked this out. This is going to be really useful !
properties (22)
authorolympicdragon
permlinkre-brianoflondon-2022324t1718479z
categoryhive-110369
json_metadata{"tags":["hive-110369","development","python","lightning","leofinance","proofofbrain","stemgeeks","podcasting2","v4vapp"],"app":"ecency/3.0.27-mobile","format":"markdown+html"}
created2022-03-24 09:18:06
last_update2022-03-24 09:18:06
depth1
children0
last_payout2022-03-31 09:18:06
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length148
author_reputation34,659,922,596,582
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,641,978
net_rshares0
@pizzabot ·
<center>PIZZA! 


PIZZA Holders sent <strong>$PIZZA</strong> tips in this post's comments:
@savvyplayer<sub>(5/10)</sub> tipped @techlhab (x1)


<sub>Learn more at https://hive.pizza.</sub></center>
properties (22)
authorpizzabot
permlinkre-lightning-keysend-is-strange-and-how-to-send-keysend-payment-in-lightning-with-the-lnd-rest-api-via-python-20220324t112325z
categoryhive-110369
json_metadata"{"app": "beem/0.24.26"}"
created2022-03-24 11:23:27
last_update2022-03-24 11:23:27
depth1
children0
last_payout2022-03-31 11:23:27
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length198
author_reputation7,539,280,605,993
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,644,591
net_rshares0
@techlhab ·
$2.79
Nice post @brianoflondon. I so much love it when I see python been utilized in building amazing solutions and softwares.

I use python alot too. And it happens to be the first language I stared with in my software and programming career.

Weldone 👍.
👍  
properties (23)
authortechlhab
permlinkre-brianoflondon-r98xjo
categoryhive-110369
json_metadata{"tags":["hive-110369"],"app":"peakd/2022.03.7"}
created2022-03-24 11:07:09
last_update2022-03-24 11:07:09
depth1
children0
last_payout2022-03-31 11:07:09
cashout_time1969-12-31 23:59:59
total_payout_value1.395 HBD
curator_payout_value1.396 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length249
author_reputation11,066,014,147,515
root_title"Lightning Keysend is strange and how to send Keysend Payment in Lightning with the LND REST API via Python"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id111,644,180
net_rshares1,949,713,630,000
author_curate_reward""
vote details (1)