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.

Table 1. List of Available APIs
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.

Table 2. List of Available APIs
Schema name Comment Prefix Namespace Link Since Version

Lightning API

Main API for LND

None

http://lightningj.org/xsd/lndjapi_1_0

lnd_v1.xsd

0.1-Beta

Wallet API

Unlocking of Wallet

None

http://lightningj.org/xsd/http://lightningj.org/xsd/walletunlocker_1_0

walletunlocker_v1.xsd

0.1-Beta

Autopilot API

Contains methods for managing autopilot

autopilot:

http://lightningj.org/xsd/autopilot_1_0

autopilot_v1.xsd

0.6-Beta-rc1

ChainNotifier API

Contains methods for the chain notifier service.

chainnotifier:

http://lightningj.org/xsd/chainnotifier_1_0

chainnotifier_v1.xsd

0.6-Beta-rc1

ChainKit API

Contains methods for the chain kit service.

chainkit:

http://lightningj.org/xsd/chainkit_1_0

chainkit_v1.xsd

0.16.2-Beta

Invoices API

Invoices is a service that can be used to create, accept, settle and cancel invoices

invoices:

http://lightningj.org/xsd/invoices_1_0

invoices_v1.xsd

0.6-Beta-rc1

Router API

Contains methods for the router service.

router:

http://lightningj.org/xsd/router_1_0

router_v1.xsd

0.6-Beta-rc1

Signer API

Contains methods for the signer service.

signer:

http://lightningj.org/xsd/signer_1_0

signer_v1.xsd

0.6-Beta-rc1

WalletKit API

Contains methods for the wallet kit service.

walletkit:

http://lightningj.org/xsd/walletkit_1_0

walletkit_v1.xsd

0.6-Beta-rc1

Peers API

Contains methods for the peers service.

peers:

http://lightningj.org/xsd/peers_1_0

peers_v1.xsd

0.15.0-Beta

Neutrino API

Contains methods for the neutrino service.

peers:

http://lightningj.org/xsd/neutrino_1_0

neutrino_v1.xsd

0.15.0-Beta

Dev API

Contains methods for the dev service.

peers:

http://lightningj.org/xsd/dev_1_0

dev_v1.xsd

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

Table 3. Types 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.

Table 4. Status Code to High Level Status Exception Mappings
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>