Lightning APIs for Java 2024-02-20
Introduction
LightningJ is a project with the intention to simplify the integration of existing Lightning node implementations for Java developers. It contains simple to use API implementations and converters between JSON, XML.
More information and source code can be found on the Github site. There it is possible to report issues and contribute code.
If you are developing using the grails framework there is also a Grails 3.3+ plugin at https://grails.lightningj.org
If you are developing Paywall functionality using Java, check out LightningJ Paywall at: https://paywall.lightningj.org
If you want updates on this project, follow https://twitter.com/LightningJ_org on twitter.
Important: This API is still in beta and API methods might change before real production release.
All use of this library is on your own risk.
What LightningJ is Not
LightningJ is not itself a Lightning Node implementation but contains APIs to easy integrate existing Lightning (currently LND) implementations using Java.
License
This library is Open Source and released under the LGPL v3 License. A link to the license agreement can be found here.
Whats New
-
0.17.4-Beta : Generated against LND 0.17.4 API, updated Updated grpc to 1.61.0 and protobuf to 3.22.5, proto-google-common-protos:2.31.0 and netty-tcnative-boringssl-static to 2.0.62.Final thanx to help from theborakompanioni.
-
0.17.2-Beta : Generated against LND 0.17.2 API
-
0.17.0-Beta : Generated against LND 0.17.0 API, upgraded to Gradle 8.1
-
0.16.2-Beta : Generated against LND 0.16.2 API, added support for ChainKit RPC Service.
-
0.16.0-Beta : Generated against LND 0.16.0 API
-
0.15.5-Beta : Generated against LND 0.15.5 and fix for NaN issue #79
-
0.15.3-Beta : Major update, Generated against 0.15.3 and updated Gradle to 7.5.1. Updated grpc to 1.49.2 and protobuf to 3.21.7. Migrated from jaxb.xml.bind to jakarta.xml.bind. Done with contribution from rachid-o.
-
0.15.1-Beta : Generated against LND 0.15.1 API and also verified to work with 0.15.2
-
0.15.0-Beta : Generated against LND 0.15.0 API, Added Dev, Peers and Neutrino RPC APIs.
-
0.14.3-Beta : Generated against LND 0.14.3 API
-
0.14.2-Beta : Generated against LND 0.14.2 API
-
0.14.1-Beta : Generated against LND 0.14.0 and 0.14.1 API
-
0.13.1-Beta : Generated against LND 0.13.1, 0.13.2, 0.13.3 API, Updated GRPC dependencies to latest versions. Tested that it works with LND 0.13.3.
-
0.13.0-Beta : Generated against LND 0.13.0 API, Added State Service API.
-
0.12.1-Beta : Generated against LND 0.12.1 API
-
0.12.0-Beta : Generated against LND 0.12.0 API
-
0.12.0-Beta-rc2: Generated against LND 0.12.0-rc2 API
-
0.11.1-Beta : Generated against LND 0.11.1 API
-
0.11.0-Beta : Generated against LND 0.11.0, WalletUnlocker API moved to package org.lightningj.lnd.wrapper.walletunlocker and also separate XSD in order to match APIs
-
0.10.1-Beta : Generated against LND 0.10.1, 0.10.2, 0.10.3 API. With updated Router GRPC API.
-
0.10.1-Beta-rc1: Generated against LND 0.10.1-rc1 API. With updated Router GRPC API.
-
0.10.0-Beta : Generated against LND 0.10.0 API
-
0.10.0-Beta-rc2: Generated against LND 0.10.0 API-rc2. Added Versioner GRPC API.
-
0.9.2-Beta : Generated against LND 0.9.2 API.
-
0.9.1-Beta : Generated against LND 0.9.1 API.
-
0.9.0-Beta : Generated against LND 0.9.0 API.
-
0.9.0-Beta-rc1 : Generated against LND 0.9.0-rc1 API.
-
0.8.2-Beta : Generated against LND 0.8.2 API. (Identical to 0.8.0 from an GRPC perspective, so using lightningJ 0.8.0 should work as well.)
-
0.8.1-Beta : Generated against LND 0.8.1 API, (Identical to 0.8.0 from an GRPC perspective, so using lightningJ 0.8.0 should work as well.)
-
0.8.0-Beta : Generated against LND 0.8.0 API
-
0.8.0-Beta-rc1 : Added build support for JDK 11 and upgraded gradlew to 5.6 and GRPC to 1.23.0. Generated API against LND 0.8.0-rc1 and fixed issue #43 of Map fields not serialized to JSON Correctly.
-
0.7.1-Beta : Generated against LND 0.7.1 API, basically the same as 0.7.1-rc1.
-
0.7.1-Beta-rc1 : Generated against LND 0.7.1-rc1 API.
-
0.7.0-Beta : Generated against LND 0.7.0 API, no major changes from rc2.
-
0.7.0-Beta-rc2 : Generated against LND 0.7.0-rc2 API and added watchtower API.
-
0.7.0-Beta-rc1 : Generated against LND 0.7.0-rc1 API.
-
0.6.1-Beta-u2 : Bugfix of wrapping bug related to new grpc u1 (see issue #33).
-
0.6.1-Beta-u1 : Bugfix release with updates of newer GRPC (1.21.0) (see issue #32).
-
0.6.1-Beta : Generated against LND 0.6.1 API.
-
0.6.1-Beta-rc1 : Generated against LND 0.6.1-rc1, no major changes.
-
0.6.0-Beta : No changes to rc4, just changed version.
-
0.6.0-Beta-rc4 : Generated API from LND 0.6.0 Beta RC4 release. No notable changes to 0.6.0-Beta-rc2.
-
0.6.0-Beta-rc2 : Generated API for LND 0.6.0 Beta RC2 and added APIs for the new Autopilot, ChainNotifier Invoices, Router, Signer and WalletKit APIs. XML Schema was also split up to have one schema for each API.
-
0.5.2-Beta : Basically the same as 0.5.2-Beta-rc3, no changes to RPC
-
0.5.2-Beta-rc3 : Generated API from LND 0.5.2 Beta RC3 release. Also changed default message max size limit in to 50 MB to reflect settings in LND.
-
0.5.1-Beta : Generated API from LND 0.5.1 Beta release.
-
0.5.1-Beta-rc2 : Generated API from LND 0.5.1 Beta RC2 release.
-
0.5.0-Beta : Generated API from LND 0.5.0 Beta release.
-
0.5.0-Beta-rc2 : Generated API from LND 0.5.0 Beta RC2 release.
-
0.5.0-Beta-rc1 : Generated API from LND 0.5.0 Beta RC1 release.
-
0.4.2-Beta-2 : Bugfix for null pointer problem generating invoices and other messages containing unset repeatable fields.
-
0.4.2-Beta : Generated API from LND 0.4.2 Beta release.
-
0.4.1-Beta : Generated API from LND 0.4.1 Beta release.
-
0.4-Beta : API generated from LDN rpc.proto for LND 0.4-Beta tag. Also check out the new grails plugin as https://grails.lightningj.org
-
0.3-Beta : Z-Base32 encoder/decoder, updated API to support new Wallet Seed generation.
-
0.2-Beta : Added support for Macaroons authentication.
-
0.1-Beta : This is the initial release with generated APIs (Synchronous and Asynchronous) for LND.
Roadmap
-
LND: Keep rpc.proto specification updated with latest LND release.
Using LightningJ Library
To use this library you can either add it as a dependency from maven central repository or build it from source.
From Maven Central
Add the following dependency to your pom.xml
<dependency>
<groupId>org.lightningj</groupId>
<artifactId>lightningj</artifactId>
<version>{project-version}</version>
</dependency>
Or to your build.gradle
implementation 'org.lightningj:lightningj:{project-version}'
All tags and releases is signed with the following GPG Key.
GPG Key Fingerprint:
7C0F 80B8 BD9F E3B8 1388 4BA1 9515 B31D DD9B BCCD
From Source
To build from source clone the repository and use gradlew to build.
git clone https://github.com/lightningj-org/lightningj.git cd lightningj ./gradlew build
The generated jars is located in build/libs.
Using the LND API
This section contains information on how to use the APIs to connect and communicate with a LND node.
The LightningJ takes the LND GRPC (gRPC Remote Procedure Calls) proto specification file (rpc.proto) and first generates the low-level GRPC API using standard GRPC Java. Then it generates a wrapping high level API and adds JSON, XML and validation features on top of the underlying GRPC message objects.
In the source there is a directory src/examples/lnd that also contains tips and tricks on how to use the API.
Note: If using sbt as build tool, there have been reported problem resolving the libraries javax.json-api and javax.json. In that case they have to be added as dependencies to sbt manually.
Getting started with LND
To get started with a LND node, see the LND developer site: http://dev.lightning.community/. There is an installation guide and a tutorial.
Using the High Level API
The high level api contains wrapper classes and a API interface for both synchronous and asynchronous calls. There is two APIs generated, the main LND API and Wallet Unlocker API.
When creating an instance it is possible to either specify the trusted SSL Certificate and the macaroon file that should be used. (If no macaroon is required by the LND node is null acceptable as parameter). Or specify a custom SSL Context and Macaroon Context for more advanced control.
For more details about each call see LND API documentation
Synchronous API
The synchronous APIs are API calls that waits for response before continuing the thread.
See section Available APIs for a list of available Asynchronous APIs.
Below is an example on how to use a Synchronous API using the main LND API.
// To create a synchronousAPI there are three constructors available
// One simple with host,port and certificate to trust, last file is the file path to the macaroon, use null if no macaroons are used.
SynchronousLndAPI synchronousLndAPI = new SynchronousLndAPI("localhost",10001,
new File("/Library/Application Support/Lnd/tls.cert"),
new File(System.getProperty("user.home")+ "/Library/Application Support/Lnd/admin.macaroon"));
// A second with host,port and a custom SSL Context for more advanced SSL Context and Macaroon Context settings.
//SynchronousLndAPI synchronousLndAPI = new SynchronousLndAPI("localhost",10001,sSLContext, macaroonContext);
// The third that takes a ManagedChannel, with full customization capabilities of underlying API
// See GRPC Java documentation for details.
//SynchronousLndAPI synchronousLndAPI = new SynchronousLndAPI(managedChannel);
// By default is validation performed on all inbound and outbound messages, to turn of validation:
//synchronousLndAPI.setPerformValidation(false);
// Example call to get channel balance and write output as JSON (pretty printed)
System.out.println(synchronousLndAPI.channelBalance().toJsonAsString(true));
// Calls returns a wrapped response or Iterator of wrapped responses.
// Example to get a response:
ListPeersResponse listPeersResponse = synchronousLndAPI.listPeers(false);
// The response can be converted to XML or JSON or just parsed.
// A more advanced call returning an iterator is for example openChannel().
// To generate a request call, there are two ways to generate a request.
// Either build up a request object like below:
OpenChannelRequest openChannelRequest = new OpenChannelRequest();
openChannelRequest.setNodePubkeyString("02ad1fddad0c572ec3e886cbea31bbafa30b5f7e745da7e936ed9d1471116cdc02");
openChannelRequest.setLocalFundingAmount(40000);
openChannelRequest.setPushSat(25000);
openChannelRequest.setSatPerByte(0);
// Alternatively it is possible to specify the parameters directly without having to create a request.
// Iterator<OpenStatusUpdate> result = synchronousLndAPI.openChannel(1,null,"02ad1fddad0c572ec3e886cbea31bbafa30b5f7e745da7e936ed9d1471116cdc02", 40000L,25000L,null,0L,null,null);
// Perform the call using alternative 1
Iterator<OpenStatusUpdate> result = synchronousLndAPI.openChannel(openChannelRequest);
// This call will wait for a the channel has opened, which means confirmation block must
// generated in btc. If simnet is used you can manually generate blocks with
// 'btcctl --simnet --rpcuser=kek --rpcpass=kek generate 3'
while (result.hasNext()) {
System.out.println("Received Update: " + result.next().toJsonAsString(true));
}
// To close the api use the method
synchronousLndAPI.close();
Asynchronous API
The asynchronous is a non-blocking API that doesn’t wait for a response but expects a StreamObserver implementation handling the response at a later time and is useful i GUI applications to give a more fluent experience.
See section Available APIs for a list of available Asynchronous APIs.
And example on how to use the main LDN Asynchronous API
// Create API, using the most simple constructor. There are alternatives
// where it is possible to specify custom SSLContext or just a managed channel.
// See SynchronousLndAPIExample for details.
AsynchronousLndAPI asynchronousLndAPI = new AsynchronousLndAPI("localhost",10001,new File("/Users/philip/Library/Application Support/Lnd/tls.cert"),null);
try {
// Example of a simple asynchronous call.
System.out.println("Sending WalletBalance request...");
asynchronousLndAPI.walletBalance(new StreamObserver<WalletBalanceResponse>() {
// Each response is sent in a onNext call.
@Override
public void onNext(WalletBalanceResponse value) {
System.out.println("Received WalletBalance response: " + value.toJsonAsString(true));
}
// Errors during the stream is showed here.
@Override
public void onError(Throwable t) {
System.err.println("Error occurred during WalletBalance call: " + t.getMessage());
t.printStackTrace(System.err);
}
// When the stream have finished is onCompleted called.
@Override
public void onCompleted() {
System.out.println("WalletBalance call closed.");
}
});
// Call to subscribe for invoices.
// To recieve invoices you can use the lncli to send payment of an invoice to your LND node.
// and it will show up here.
System.out.println("Subscribing to invoices call...");
asynchronousLndAPI.subscribeInvoices(new StreamObserver<Invoice>() {
@Override
public void onNext(Invoice value) {
System.out.println("Received Invoice: " + value.toJsonAsString(true));
}
@Override
public void onError(Throwable t) {
System.err.println("Error occurred during subscribeInvoices call: " + t.getMessage());
t.printStackTrace(System.err);
}
@Override
public void onCompleted() {
System.out.println("subscribeInvoices call closed.");
}
});
System.out.println("Press Ctrl-C to stop listening for invoices");
while (true) {
Thread.sleep(1000);
}
} finally {
// To close the api use the method
asynchronousLndAPI.close();
}
Available APIs
Starting from 0.6.0 there are several different APIs to the different services.
API Name | Comment | Syncronious API Class | Asynchronous API Class | Since Version |
---|---|---|---|---|
Lightning API |
Main API for LND |
org.lightningj.lnd.wrapper.SynchronousLndAPI |
org.lightningj.lnd.wrapper.AsynchronousLndAPI |
0.1-Beta |
Wallet API |
Unlocking of Wallet |
org.lightningj.lnd.wrapper.walletunlocker.SynchronousWalletUnlockerAPI |
org.lightningj.lnd.wrapper.walletunlocker.AsynchronousWalletUnlockerAPI |
0.11-Beta |
Autopilot API |
Contains methods for managing autopilot |
org.lightningj.lnd.wrapper.autopilot.SynchronousAutopilotAPI |
org.lightningj.lnd.wrapper.autopilot.AsynchronousAutopilotAPI |
0.6-Beta-rc1 |
ChainNotifier API |
Contains methods for the chain notifier service. |
org.lightningj.lnd.wrapper.chainnotifier.SynchronousChainNotifierAPI |
org.lightningj.lnd.wrapper.chainnotifier.AsynchronousChainNotifierAPI |
0.6-Beta-rc1 |
Invoices API |
Invoices is a service that can be used to create, accept, settle and cancel invoices |
org.lightningj.lnd.wrapper.invoices.SynchronousInvoicesAPI |
org.lightningj.lnd.wrapper.invoices.AsynchronousInvoicesAPI |
0.6-Beta-rc1 |
Router API |
Contains methods for the router service. |
org.lightningj.lnd.wrapper.router.SynchronousRouterAPI |
org.lightningj.lnd.wrapper.router.AsynchronousRouterAPI |
0.6-Beta-rc1 |
Signer API |
Contains methods for the signer service. |
org.lightningj.lnd.wrapper.signer.SynchronousSignerAPI |
org.lightningj.lnd.wrapper.signer.AsynchronousSignerAPI |
0.6-Beta-rc1 |
WalletKit API |
Contains methods for the wallet kit service. |
org.lightningj.lnd.wrapper.walletkit.SynchronousWalletKitAPI |
org.lightningj.lnd.wrapper.walletkit.AsynchronousWalletKitAPI |
0.6-Beta-rc1 |
WalletKit API |
Contains methods for the wallet kit service. |
org.lightningj.lnd.wrapper.watchtower.SynchronousWatchtowerAPI |
org.lightningj.lnd.wrapper.watchtower.AsynchronousWatchtowerAPI |
0.7.0-rc2 |
Watchtower Client API |
Contains methods for the wallet kit service. |
org.lightningj.lnd.wrapper.wtclient.SynchronousWatchtowerClientAPI |
org.lightningj.lnd.wrapper.wtclient.AsynchronousWatchtowerClientAPI |
0.7.0-rc2 |
Versioning API |
Contains methods for the versioning (verrpc) service. |
org.lightningj.lnd.wrapper.verrpc.SynchronousVersionerAPI |
org.lightningj.lnd.wrapper.verrpc.AsynchronousVersionerAPI |
0.10-Beta-rc2 |
Peers API |
Contains methods for the peers (peerrpc) service. |
org.lightningj.lnd.wrapper.peers.SynchronousPeersAPI |
org.lightningj.lnd.wrapper.peers.AsynchronousPeersAPI |
0.15.0-Beta |
Neutrino API |
Contains methods for the neutrino (neutrinrpc) service. |
org.lightningj.lnd.wrapper.neutrino.SynchronousNeutrinoAPI |
org.lightningj.lnd.wrapper.neutrino.AsynchronousNeutrinoAPI |
0.15.0-Beta |
Dev API |
Contains methods for the dev (devrpc) service. |
org.lightningj.lnd.wrapper.dev.SynchronousDevAPI |
org.lightningj.lnd.wrapper.dev.AsynchronousDevAPI |
0.15.0-Beta |
Json Conversion
The library uses the JSR 374 javax.json api to generate and parse JSON.
Converting between JSON and High Level API objects is pretty straight forward as shown in following example:
// Get API
SynchronousLndAPI synchronousLndAPI = getSynchronousLndAPI();
// To convert JSON request data to a wrapped request object (High level)
// Do the following
String jsonData = "{\"node_pubkey\":\"\",\"node_pubkey_string\":\"02ad1fddad0c572ec3e886cbea31bbafa30b5f7e745da7e936ed9d1471116cdc02\",\"local_funding_amount\":40000,\"push_sat\":25000,\"targetConf\":0,\"satPerByte\":0,\"private\":false,\"min_htlc_msat\":0}";
// The library uses the javax.json-api 1.0 (JSR 374) API to parse and generate JSON.
// To parse a JSON String, start by creating a JsonReader
JsonReader jsonReader = Json.createReader(new StringReader(jsonData));
// Then parse by creating a Wrapped Message object.
OpenChannelRequest openChannelRequest = new OpenChannelRequest(jsonReader);
// Perform the call.
Iterator<OpenStatusUpdate> result = synchronousLndAPI.openChannel(openChannelRequest);
// This call will wait for a the channel has opened, which means confirmation block must
// generated in btc. If simnet is used you can manually generate blocks with
// 'btcctl --simnet --rpcuser=kek --rpcpass=kek generate 3'
while (result.hasNext()) {
// To generate JSON from a response there are three possiblities, either
OpenStatusUpdate next = result.next();
// To get JSON as String
System.out.println("Received Update: " + next.toJsonAsString(false));
// To have the result more human readable set pretty print to true
System.out.println("Received Update: " + next.toJsonAsString(true));
// It is also possible to get the JSON as a populated JsonObjectBuilder
JsonObjectBuilder jsonObjectBuilder = next.toJson();
}
XML Conversion
For XML parsing and generation is JAXB used. And to convert between XML data and high level wrapper object is a XMLParser used.
Use XMLParserFactory to retrieve a XMLParser for the used XML Schema version (currently only version "1.0" exist and should still not be considered final and could change until LND releases a final release.)
An example on XML conversion:
// Get API
SynchronousLndAPI synchronousLndAPI = getSynchronousLndAPI();
// Create a XMLParserFactory
XMLParserFactory xmlParserFactory = new XMLParserFactory();
// Retrieve XML Parser for a given XML version schema. (Currently "1.0")
XMLParser xmlParser = xmlParserFactory.getXMLParser("1.0");
byte[] xmlRequestData = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><OpenChannelRequest xmlns=\"http://lightningj.org/xsd/lndjapi_1_0\"><nodePubkey></nodePubkey><nodePubkeyString>02ad1fddad0c572ec3e886cbea31bbafa30b5f7e745da7e936ed9d1471116cdc02</nodePubkeyString><localFundingAmount>40000</localFundingAmount><pushSat>25000</pushSat><targetConf>0</targetConf><satPerByte>0</satPerByte><private>false</private><minHtlcMsat>0</minHtlcMsat></OpenChannelRequest>".getBytes("UTF-8");
// Convert to a wrapped high level message object.
OpenChannelRequest openChannelRequest = (OpenChannelRequest) xmlParser.unmarshall(xmlRequestData);
// Perform the call.
Iterator<OpenStatusUpdate> result = synchronousLndAPI.openChannel(openChannelRequest);
// This call will wait for a the channel has opened, which means confirmation block must
// generated in btc. If simnet is used you can manually generate blocks with
// 'btcctl --simnet --rpcuser=kek --rpcpass=kek generate 3'
while(result.hasNext()){
// To generate XML from a response do the following:
OpenStatusUpdate next = result.next();
// To get XML as byte[]
byte[] responseData = xmlParser.marshall(next);
System.out.println("XML Response data: " + new String(responseData,"UTF-8"));
// To get XML pretty printed
byte[] responseDataPrettyPrinted = xmlParser.marshall(next,true);
System.out.println("Pretty Printed XML Response data: " + new String(responseDataPrettyPrinted,"UTF-8"));
}
In 0.6.0 was the schema updated and there exists several schemas for each separate service. See table below for link to each schema, namespace and default prefix. in 0.11 is the walletunlocker API moved to its own XSD.
Schema name | Comment | Prefix | Namespace | Link | Since Version |
---|---|---|---|---|---|
Lightning API |
Main API for LND |
None |
0.1-Beta |
||
Wallet API |
Unlocking of Wallet |
None |
http://lightningj.org/xsd/http://lightningj.org/xsd/walletunlocker_1_0 |
0.1-Beta |
|
Autopilot API |
Contains methods for managing autopilot |
autopilot: |
0.6-Beta-rc1 |
||
ChainNotifier API |
Contains methods for the chain notifier service. |
chainnotifier: |
0.6-Beta-rc1 |
||
ChainKit API |
Contains methods for the chain kit service. |
chainkit: |
0.16.2-Beta |
||
Invoices API |
Invoices is a service that can be used to create, accept, settle and cancel invoices |
invoices: |
0.6-Beta-rc1 |
||
Router API |
Contains methods for the router service. |
router: |
0.6-Beta-rc1 |
||
Signer API |
Contains methods for the signer service. |
signer: |
0.6-Beta-rc1 |
||
WalletKit API |
Contains methods for the wallet kit service. |
walletkit: |
0.6-Beta-rc1 |
||
Peers API |
Contains methods for the peers service. |
peers: |
0.15.0-Beta |
||
Neutrino API |
Contains methods for the neutrino service. |
peers: |
0.15.0-Beta |
||
Dev API |
Contains methods for the dev service. |
peers: |
0.15.0-Beta |
Validation
The library also have a validation functionality to validate messages. It uses the underlying proto specification to check that each field has accepted values. Currently there are not that many validation related parameters specified in the rpc.proto but might improve in the future that will make the validation parts of the library more useful.
Below is an example of how validation can be done:
// Get API
SynchronousLndAPI synchronousLndAPI = getSynchronousLndAPI();
// To manually validate a wrapped Message it is possible to call the validate() method.
OpenChannelRequest openChannelRequest = genOpenChannelRequest();
// To validate call validate() and it will return ValidationResult
ValidationResult validationResult = openChannelRequest.validate();
// The ValidationResult.isValid() returns true if the message was valud.
validationResult.isValid();
// If there is problems it is possible to retrieve the problems found either
// in a single aggregated list for all sub-messages.
List<ValidationProblems> allProblems= validationResult.getAggregatedValidationErrors();
// Or as a tree structure with all problems in this message in:
validationResult.getMessageErrors();
// and all sub messages as their own report.
validationResult.getSubMessageResults();
try {
// Each call might throw a ValidationException
synchronousLndAPI.channelBalance();
} catch (ValidationException ve) {
// A ValidationException has the faulty messages ValidationReport as a field.
ValidationResult vr = ve.getValidationResult();
} catch (StatusException se) {
//...
}
Validation Internationalization
Each ValidationProblem has a translatable message resource key as a field. The message resource file bundle is in src/main/resources/lightningj_messages
Exception Handling
High Level API
The High Level API has two categories of exceptions that can be thrown during an API
call. One is ValidationException indicating that a message didn’t conform to GRPC Proto
specification. The other category consist of a base StatusException, (wrapping the low level
io.grpc.StatusException
or io.grpc.StatusRuntimeException
), and three sub exception
indicating the type of status problem that occurred and that could be handled differently.
Here is a list of status exceptions
Exception | Description |
---|---|
StatusException |
Base exception for all types of GRPC related problems. |
ClientSideException |
Indicate there is some problem on the client side such as invalid request data. |
ServerSideException |
Indicate there is some problem on the server side that might persist for some time. |
CommunicationException |
This could indicate timeout or dropped package and request can be retried. |
So when calling an API call you can either choose to just handle ValidationException or StatusException or to do more fine pruned error handling by managing ClientSideException, ServerSideException or CommunicationException separately.
// Get API
SynchronousLndAPI synchronousLndAPI = getSynchronousLndAPI();
try {
// Perform a call
synchronousLndAPI.channelBalance();
} catch (ValidationException ve) {
// Thrown if request or response contained invalid data
} catch (StatusException se) {
// Thrown if GRPC related exception happened.
}
// Example of more fine grained exception handling.
try {
synchronousLndAPI.channelBalance();
} catch (ValidationException ve) {
// Thrown if request or response contained invalid data
} catch (ClientSideException cse) {
// Thrown if there is some problem on the client side such as invalid request data.
} catch (ServerSideException sse) {
// Thrown if there is some problem on the server side that might persist for some time.
} catch (CommunicationException ce) {
// Thrown if communication problems occurred such as timeout or dropped package and request can be retried.
}
AsynchronousLndAPI asynchronousLndAPI = getAsynchronousLndAPI();
asynchronousLndAPI.channelBalance(new StreamObserver<ChannelBalanceResponse>() {
@Override
public void onNext(ChannelBalanceResponse value) {
// Handle ok resonses
}
@Override
public void onError(Throwable t) {
// Here is exceptions sent of same type as thrown by synchronous API.
}
@Override
public void onCompleted() {
// Call completed
}
});
Status Code to High Level Status Exception Mappings
Below is a table detailing which high level excpetion is thrown for a given status code.
Status Code | Exception |
---|---|
CANCELLED |
ClientSideException |
UNKNOWN |
ServerSideException |
INVALID_ARGUMENT |
ClientSideException |
DEADLINE_EXCEEDED |
CommunicationException |
NOT_FOUND |
ClientSideException |
ALREADY_EXISTS |
ClientSideException |
PERMISSION_DENIED |
ClientSideException |
RESOURCE_EXHAUSTED |
ServerSideException |
FAILED_PRECONDITION |
ServerSideException |
ABORTED |
ServerSideException |
OUT_OF_RANGE |
ClientSideException |
UNIMPLEMENTED |
ServerSideException |
INTERNAL |
ServerSideException |
UNAVAILABLE |
CommunicationException |
DATA_LOSS |
ServerSideException |
UNAUTHENTICATED |
ClientSideException |
Low Level API
The low level API throws either the io.grpc.StatusException and io.grpc.StatusRuntimeException when problems occur containing a Status value. See GRPC Java documentation for more details.
Logging
The library uses the standard java.logging API for logging. Which is the same library as the underlying GRPC Java uses.
It has one Logger defined "org.lightningj.lnd.wrapper.API" and it is possible to setting it to
LogLevel.FINE
to have incoming and outgoing messages logged in pretty printed JSON format
to help out when debugging.
Using the Low Level API Directly
If performance is most important and there is no need for JSON/XML convertion in your project you can use the auto-generated GRPC API directly.
It is generated from the LND rpc.proto specification and contains all supported messages and calls.
Example for using the low level API :
File trustedServerCertificate = new File(System.getProperty("user.home") + "/Library/Application Support/Lnd/tls.cert");
// Method to create SSL Context, trusting a specified LND node TLS certificate.
// It is possible to customize the SSL setting by supplying a javax.net.ssl.SSLContext as well
SslContext sslContext = GrpcSslContexts.configure(SslContextBuilder.forClient(), SslProvider.OPENSSL)
.trustManager(trustedServerCertificate)
.build();
// Then create a managed communication channed
ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", 10001)
.sslContext(sslContext)
.build();
// Then create the low level API by calling.
LightningGrpc.LightningBlockingStub stub = LightningGrpc.newBlockingStub(channel);
// To create asynchronous API us LightningGrpc.newStub(channel)
// Create a request object using messages in "org.lightningj.lnd.proto.LightningApi"
LightningApi.WalletBalanceRequest.Builder walletBalanceRequest = LightningApi.WalletBalanceRequest.newBuilder();
walletBalanceRequest.setWitnessOnly(true);
try {
LightningApi.WalletBalanceResponse response = stub.walletBalance(walletBalanceRequest.build());
System.out.println("Wallet Balance: " + response.getTotalBalance());
} catch (StatusRuntimeException sre) {
// Handle exceptions a with status code in sre.getStatus()
}
More info about using GRPC Java API can be found at their Github or a their tutorial site.
JavaDoc API Documentation
The LightningJ JavaDoc API Reference can be found here.
Dependencies
A dependency report on dependent JAR files can be found here.
To view the requirements for run-time see the runtime section.
The JSON Libraries is built upon JSR 374 and probably can the glassfish dependency be replaced with whatever JSR 374 compliant implementation used by your container.
Using Intellij
If using LigtningJ source code with Intellij, there can be a problem with the generated low level API class files being too large.
To fix this must the accepted file size be enhanced. This can be done by:
-
In Menu: Help → Edit Custom Properties
-
In idea.properties add:
idea.max.intellisense.filesize=8000
-
Restart IntelliJ
Test Reports
A report of performed unit tests of the API can be found here.
For LightningJ Developers
LightningJ is a Java project built using Gradle. Unit tests is written using Groovy and Spock Framework.
To build the project use:
./gradlew build
The build jar file is located in build/libs.
To generate documentation use:
./gradlew build doc
This will generate documentation in build/docs/html5.
To clean the project use:
./gradlew build doc
How to update rpc.proto file
-
Download the file from the LND repository:
lnd/lnrpc/rpc.proto
-
Update file into src/main/proto/lightning.api.proto
-
In the header of the file below 'package lnrpc' add:
option java_package = "org.lightningj.proto";
-
Then run
./gradlew clean build
GPG Sign Releases using SmartCard
To GPG Sign generated archives before publishing them to central repository using GPG Smartcard make sure to configure the following in ~/.gradle/gradle.properties
signing.gnupg.executable=gpg2 signing.gnupg.useLegacyGpg=false signing.gnupg.keyName=<your key id>