Lightning Quick Draw Game With HOLD Invoices
HOLD invoices are a great addition to the LND lightning implementation stack and open up a range of new use cases.
In a nutshell HOLD invoices allow a payer to make a payment to an invoice in which the payee can withheld the final state/completion of the payment until they decide to cancel or settle the payment. If they cancel the payment the amount is returned back to the payer, if they settle they complete the payment and receive the funds. It seems the initial use case for HOLD invoice is in a merchant situation.
For example using a typical lightning invoice if a user makes a payment for a hat and after the payment has completed the merchant realizes the hat is out of stock, in this scenario the merchant needs to contact the user ask them to create an invoice and then return the funds. With hold invoices the merchant can simply cancel the payment if the item is out of stock or settle the payment if the item is in stock and after the item has shipped.
How HOLD invoices work
HOLD invoices work by allowing the user to first create a pre image (a random 32byte hexadecimal number) and pass it’s hash when creating an invoice. After the invoice has been created and paid the user must then reveal the pre image in order to settle/sweep the payment.
If we don’t allow the user to decide the pre image but let a 3rd party that is not the payee nor the payer we can create a simple betting type game, or in this case a Western Style Quick Draw for mobile!
Example Quick Draw Game
Player 1 “Rattle Snake Alice” and Player 2 “Buffalo Bob” download the “Western Quick Draw” game onto their mobile device, their mobile device also has a lightning wallet.
The game then links the players via bluetooth and creates the pre image which it keeps secret from the players. The game then hashes the pre image and passes the hash to the players. The players use this hash to create a hold invoice for $1. They then pass their invoices to each other and pay each others invoices.
At this point each player has paid the $1 and cannot revoke the payment, however neither payer is able to settle the funds as only the game knows the pre image. Effectively the game is escrowing the payments by holding the pre image however it is not holding the actual funds and if the game were to error or go offline the funds could be returned to the players after the invoices expire (Lightning Invoices have an expiry parameter).
Once the game has determined that both players have paid each other’s invoice it starts the dual. The user who fires first wins and the game only releases the pre image to the winning player allowing them to sweep the funds paid by the loser. It is also considered good sport if the loser cancels their invoice so the winner can receive their withheld paid funds without needing to wait for the loser’s invoice to expire. Because of this a short expiry time is recommended.
A video of the game being played can be seen below
Development (warning! boring technical details ahead)
Game
The game was created using Unity 3D
Wallet
As the game is mobile it was required to have a mobile wallet that could interact with it. I used the Pebble Wallet as a base, pebble wallet is an open source mobile wallet built on top of LND. However Pebble wallet did not have
HOLD invoices implemented so it was necessary to add it.
In order to add HOLD invoice I had build LND for mobile using gomobile with the make flag
make tags="invoicesrpc"
After LND is built with this tag according to the HOLD invoice documentation it is possible to create/cancel and settle HODL invoices using the following commands
lncli addholdinvoice <hash> --amt <amt>
lncli cancelinvoice <hash>
lncli settleinvoice <preimage>
The status of a HOLD invoice can be determined using either the SubscribeSingleInvoice call which will return events as the status of the invoice changes or can be checked using polling and
lncli lookupinvoice <hash>
However we cannot use lncli as our LND is running in a mobile wallet therefore it is required to use GRPC (a protobuf api used by LND) to make requests. Unfortunately LND running in mobile does not use just GRPC to communicate with the LND rpc, this is because such a connection would likely be terminated by the mobile OS when the app enters the background, furthermore using GRPC would result in LNDs rpcserver running on the mobile devices localhost which is not desirable. Hence LND running on mobile actually creates native bindings from the protobufs to allow a mobile app to directly communicate with the rpc server using memory buffer sockets. More info on this can be seen here in a talk by Johan Torås Halseth of Lightning Labs.
Moving on I had to create the mobile bindings for the HOLD invoices my self in a special branch of LND I created here, from this I could build a native LNDMobile library which I could import into pebble wallet.
Finally in pebble wallet I had to write some connectivity logic to allow a the mobile Unity 3D game to communicate with the mobile LND wallet in order to request and create invoices. The specific details of interest can be found on the repo here
Game
The game was developed using UNITY 3D and its repo can be found here, it uses a bluetooth module to let the two devices connect to each other and pass various requests such as their invoices, hashes, when a player shot etc
The game also uses android intents to allow it to connect with the wallet which handles the lightning payments and invoice creation.
It was also required to use some cryptography in order to generate the pre image and its hash for the hold invoice
void createInvoice()
{
using (RandomNumberGenerator rng = new RNGCryptoServiceProvider())
{
byte[] preimage = new byte[32];
rng.GetBytes(preimage);
myPreimage = BitConverter.ToString(preimage).Replace("-", string.Empty);
// Create a SHA256
using (SHA256 sha256Hash = SHA256.Create())
{
// ComputeHash - returns byte array
byte[] bytes = sha256Hash.ComputeHash(preimage);
// Convert byte array to a string
StringBuilder builder = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
builder.Append(bytes[i].ToString("x2"));
}
myHash = builder.ToString();
Debug.Log("hash is "+ myHash);
shouldCheckIntent = true;
var req = "lightning:addholdinvoice?hash=" + myHash + "&amt=" + myBet + "&expiry=3600&message=" + myBet + "%20sat%20claimed%20by%20the%20Quick%20Draw%21&package=" + Application.identifier + "&allow_pay=" + myBet;
try
{
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject packageManager = currentActivity.Call<AndroidJavaObject>("getPackageManager");
AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri");
AndroidJavaObject uriData = uriClass.CallStatic<AndroidJavaObject>("parse", req);
AndroidJavaObject i = packageManager.Call<AndroidJavaObject>("getLaunchIntentForPackage", "com.indiesquare.pebble");
i.Call<AndroidJavaObject>("setAction", "android.intent.action.VIEW");
i.Call<AndroidJavaObject>("setData", uriData);
currentActivity.Call("startActivity", i);
}
catch (System.Exception e)
{
Debug.Log(e);
}
}
}
}
Caveats
The main caveat is that this game requires a custom lightning mobile wallet, this wallet did not exists so I had to develop it with pebble wallet. Pebble allows a user to connect to their LND node on their server or at their home however in this case it is required that the user has rebuilt LND with the
make tags="invoicesrpc"
in order for it to be able to create and monitor HOLD invoices.
As this a barrier to entry pebble also has the ability to run LND locally via neutrino, however at current time of writing there are some small stability issues with this option such as syncing times channel graph mapping etc that hurt the user experience.
Moving Forward
My short term focus for the game is to improve stability with pebble and the local running wallet to make it possible for any user to download the game and pebble wallet without having to worry about rebuilding their hosted LND node at home or on their server.
After this is done I will focus on iOS however it is likely that Apple may not approve of this betting app.
I would also like to make a VR version but my main focus is mobile.
Ideally I envision the game to be a social app and therefore to be used in person between bitcoiners at bitcoin meet ups, conferences and events.
How To Try The App
If you would like to try the app and until pebble with the local wallet feature is released the best way to do it is to have an LND which is built with make tags=’invoicesrpc’ and can be accessed remotely. You can then download the pebble android beta version and the QuickDraw beta apk from this link.