-
Notifications
You must be signed in to change notification settings - Fork 912
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
First basic draft version of a java client library. need feedback #2223
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Draft version of the JLightning rpc interface for c-lightning. | ||
|
||
Using this client library is is as simple as the following code: | ||
``` | ||
public static void main(String[] args) { | ||
JLightningRpc rpc_interface = new JLightningRpc("/tmp/spark-env/ln1/lightning-rpc"); | ||
String res = rpc_interface.listInvoices(null); | ||
System.out.println(res); | ||
res = rpc_interface.listFunds(); | ||
System.out.println(res); | ||
} | ||
``` | ||
|
||
This client library is provided and maintained by Rene Pickhardt |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package de.renepickhardt.ln; | ||
|
||
/** | ||
* JLightning a small test / example class to demonstrate how to use JLightningRpc | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe move this into a |
||
* | ||
* This file is basically a port of the pylightning python client library | ||
* that comes with c-lightning. | ||
* | ||
* The Author of this Java Client library is Rene Pickhardt. | ||
* He also holds the copyright of this file. The library is licensed with | ||
* a BSD-style license. Have a look at the LICENSE file. | ||
* | ||
* If you like this library consider a donation via bitcoin or the lightning | ||
* network at http://ln.rene-pickhardt.de | ||
* | ||
* @author Rene Pickhardt | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit - extra line break. |
||
*/ | ||
|
||
public class JLightning { | ||
|
||
public static void main(String[] args) { | ||
// TODO Auto-generated method stub | ||
|
||
JLightningRpc rpc_interface = new JLightningRpc("/tmp/spark-env/ln1/lightning-rpc"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would inject this |
||
String res = rpc_interface.listInvoices(null); | ||
System.out.println(res); | ||
res = rpc_interface.listFunds(); | ||
System.out.println(res); | ||
res = rpc_interface.listInvoices(null); | ||
System.out.println(res); | ||
|
||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package de.renepickhardt.ln; | ||
/** | ||
* JLightningRpc extends the UnixDomainSocketRpc and exposes the specific | ||
* API that is provided by c-lightning. It is a java client library for the | ||
* c-lightning node. It connects to c-lightning via a Unix Domain Socket over | ||
* JsonRPC v2.0 | ||
* | ||
* This file is basically a port of the pylightning python client library | ||
* that comes with c-lightning. | ||
* | ||
* The Author of this Java Client library is Rene Pickhardt. | ||
* He also holds the copyright of this file. The library is licensed with | ||
* a BSD-style license. Have a look at the LICENSE file. | ||
* | ||
* If you like this library consider a donation via bitcoin or the lightning | ||
* network at http://ln.rene-pickhardt.de | ||
* | ||
* @author Rene Pickhardt | ||
*/ | ||
|
||
import java.util.HashMap; | ||
|
||
|
||
|
||
public class JLightningRpc extends UnixDomainSocketRpc { | ||
|
||
public JLightningRpc(String socket_path) { | ||
super(socket_path); | ||
|
||
} | ||
|
||
/** | ||
* Delete unpaid invoice {label} with {status} | ||
* @param label of the invoice | ||
* @param status status of the invoice | ||
* @return | ||
*/ | ||
public String delInvoice(String label, Status status) { | ||
HashMap<String, String> payload = new HashMap<String,String> (); | ||
payload.put("label", label); | ||
payload.put("status", status.toString()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To handle |
||
return this.call("delinvoice", payload); | ||
} | ||
|
||
public String getInfo() { | ||
return this.call("getinfo", null); | ||
} | ||
|
||
/** | ||
* Show route to {id} for {msatoshi}, using {riskfactor} and optional | ||
* {cltv} (default 9). If specified search from {fromid} otherwise use | ||
* this node as source. Randomize the route with up to {fuzzpercent} | ||
* (0.0 -> 100.0, default 5.0) using {seed} as an arbitrary-size string | ||
* seed. | ||
* | ||
* @param peer_id | ||
* @param msatoshi | ||
* @param riskfactor | ||
* @param cltv | ||
* @param from_id | ||
* @param fuzzpercent | ||
* @param seed | ||
* @return | ||
*/ | ||
public String getRoute(String peer_id, int msatoshi, float riskfactor, int cltv, String from_id, float fuzzpercent, String seed) { | ||
HashMap<String, String> payload = new HashMap<String,String> (); | ||
payload.put("id", peer_id); | ||
payload.put("msatoshi", Integer.toString(msatoshi)); | ||
payload.put("riskfactor", Float.toString(riskfactor)); | ||
payload.put("cltv", Integer.toString(cltv)); | ||
payload.put("fromid", from_id); | ||
payload.put("fuzzpercent", Float.toString(fuzzpercent)); | ||
payload.put("seed", seed); | ||
return this.call("getroute", payload); | ||
} | ||
|
||
/** | ||
* Create an invoice for {msatoshi} with {label} and {description} with | ||
* optional {expiry} seconds (default 1 hour) | ||
* | ||
* @param msatoshi | ||
* @param label | ||
* @param description | ||
* @param expiry | ||
* @param fallbacks | ||
* @param preimage | ||
* @return | ||
*/ | ||
public String invoice (int msatoshi,String label, String description, int expiry,String fallbacks, String preimage) { | ||
HashMap<String, String> payload = new HashMap<String,String> (); | ||
payload.put("msatoshi", Integer.toString(msatoshi)); | ||
payload.put("label", label); | ||
payload.put("description", description); | ||
payload.put("expiry", Integer.toString(expiry)); | ||
payload.put("fallbacks", fallbacks); | ||
payload.put("preimage", preimage); | ||
return this.call("invoice", payload); | ||
} | ||
|
||
/** | ||
* Show funds available for opening channels and open channels | ||
* @return | ||
*/ | ||
public String listFunds() { | ||
return this.call("listfunds", null); | ||
} | ||
|
||
/** | ||
* Show all known channels, accept optional {short_channel_id} | ||
*/ | ||
public String listChannels(String short_channel_id) { | ||
HashMap<String, String> payload = new HashMap<String,String> (); | ||
payload.put("short_channel_id", short_channel_id); | ||
return this.call("listchannels", payload); | ||
} | ||
|
||
/** | ||
* Show invoice {label} (or all, if no {label)) | ||
* @param label for a specific invoice to look up | ||
* @return | ||
*/ | ||
public String listInvoices(String label) { | ||
HashMap<String, String> payload = new HashMap<String,String> (); | ||
payload.put("label", label); | ||
return this.call("listinvoices", payload); | ||
} | ||
|
||
public String listNodes(String node_id) { | ||
HashMap<String, String> payload = new HashMap<String,String> (); | ||
payload.put("id", node_id); | ||
return this.call("listnodes", payload); | ||
} | ||
|
||
/** | ||
* Wait for the next invoice to be paid, after {lastpay_index} | ||
* (if supplied) | ||
* @param last_payindex | ||
* @return | ||
*/ | ||
public String waitAnyInvoice(int last_payindex) { | ||
HashMap<String, String> payload = new HashMap<String,String> (); | ||
payload.put("last_pay_index", Integer.toString(last_payindex)); | ||
return this.call("waitanyinvoice", payload); | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package de.renepickhardt.ln; | ||
/** | ||
* Named enum to encode the Status of invoices and payments | ||
* | ||
* This file is basically a port of the pylightning python client library | ||
* that comes with c-lightning. | ||
* | ||
* The Author of this Java Client library is Rene Pickhardt. | ||
* He also holds the copyright of this file. The library is licensed with | ||
* a BSD-style license. Have a look at the LICENSE file. | ||
* | ||
* If you like this library consider a donation via bitcoin or the lightning | ||
* network at http://ln.rene-pickhardt.de | ||
* | ||
* @author rpickhardt | ||
*/ | ||
public enum Status { | ||
PAID("paid"), UNPAID("unpaid"), EXPIRED("expired"); | ||
|
||
private final String statusDescription; | ||
|
||
private Status(String value) { | ||
statusDescription = value; | ||
} | ||
|
||
public String getStatusDescription() { | ||
return statusDescription; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package de.renepickhardt.ln; | ||
/** | ||
* UnixDomainSocketRpc the base class to handle communication between | ||
* JLightning and the c-lightning node over the UnixDomainSocket | ||
* | ||
* This file is basically a port of the pylightning python client library | ||
* that comes with c-lightning. | ||
* | ||
* The Author of this Java Client library is Rene Pickhardt. | ||
* He also holds the copyright of this file. The library is licensed with | ||
* a BSD-style license. Have a look at the LICENSE file. | ||
* | ||
* If you like this library consider a donation via bitcoin or the lightning | ||
* network at http://ln.rene-pickhardt.de | ||
* | ||
* @author Rene Pickhardt | ||
*/ | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.InputStreamReader; | ||
import java.io.OutputStream; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
|
||
import org.json.JSONException; | ||
import org.json.JSONObject; | ||
import org.newsclub.net.unix.AFUNIXSocket; | ||
import org.newsclub.net.unix.AFUNIXSocketAddress; | ||
|
||
public class UnixDomainSocketRpc { | ||
protected AFUNIXSocket sock; | ||
protected InputStream is; | ||
protected OutputStream os; | ||
protected static int id = 1; | ||
|
||
public UnixDomainSocketRpc(String socket_path) { | ||
File socketFile = new File(socket_path); | ||
try { | ||
this.sock = AFUNIXSocket.newInstance(); | ||
this.sock.connect(new AFUNIXSocketAddress(socketFile)); | ||
this.is = sock.getInputStream(); | ||
this.os = sock.getOutputStream(); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
public String call(String method, HashMap<String, String> payload) { | ||
// if no payload is given make empty one | ||
if(payload == null) { | ||
payload = new HashMap<String, String>(); | ||
} | ||
|
||
//remove null items from payload | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no need to add the keys to a new Here's some sample code:
And output is:
|
||
Set<String> keySet = new HashSet<String>(); | ||
for (String key: payload.keySet()) { | ||
keySet.add(key); | ||
} | ||
for(String k:keySet) { | ||
if(payload.get(k)==null){ | ||
payload.remove(k); | ||
} | ||
} | ||
|
||
JSONObject json = new JSONObject(); | ||
|
||
try { | ||
json.put("method", method); | ||
json.put("params", new JSONObject(payload)); | ||
// FIXME: Use id field to dispatch requests | ||
json.put("id", Integer.toString(this.id++)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} catch (JSONException e) { | ||
// TODO Auto-generated catch block | ||
e.printStackTrace(); | ||
} | ||
|
||
try { | ||
this.os.write(json.toString().getBytes("UTF-8")); | ||
this.os.flush(); | ||
|
||
// FIXME: Using a StringBuilder would be preferable but not so easy | ||
String response = ""; | ||
int buffSize = 1024; | ||
byte[] buf = new byte[buffSize]; | ||
|
||
while(true) { | ||
int read = this.is.read(buf); | ||
response = response + new String(buf, 0, read, "UTF-8"); | ||
if(read <=0 || response.contains("\n\n" )) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in my Haskell implementation I count open and closed curly brackets (ensuring they sum to 0) to determine when to stop reading. This could fail in an unlikely event that both There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Response is the string that has been appended with the buffer so I see no problem with split \n On the other side the sum of open and closed brakets might fail as those could be part of the strings in the json data. So those need to be ignored There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah good point on the curly's. I see now that it gets appended before the check so yeah looks like it would be fine. I should probably do something similar... I do find this part a bit strange. It looks like lightning-cli does it by parsing tokens as they come in, and ends the reading when there is no more to parse. |
||
break; | ||
} | ||
} | ||
json = new JSONObject(response); | ||
if (json.has("result")) { | ||
json = json.getJSONObject("result"); | ||
return json.toString(2); | ||
} | ||
else if (json.has("error")) { | ||
json = json.getJSONObject("error"); | ||
return json.toString(2); | ||
} | ||
else | ||
return "Could not Parse Response from Lightning Node: " + response; | ||
|
||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
// FIXME: make json rpc? | ||
return "no Response from lightning node"; | ||
} catch (JSONException e) { | ||
e.printStackTrace(); | ||
return "Could not parse response from Lightning Node"; | ||
} | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even if someone is familiar with java, would be nice to have the actual command to build and launch: e.g.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On that note, have you though of using Gradle or Maven to handle building and dependency management? Preferably Gradle