Skip to content

blockfirm/pine-lightning

Repository files navigation

Pine Lightning

A bridge between a customized version of lnd and the Pine app for signing transactions using keys owned by the user residing on the user's device.

Table of Contents

Dependencies

  • Node.js (v12) and gRPC for creating the RPC API
  • Pine lnd as lightning node without private keys
  • Redis for caching channel info to be used by the Pine Payment Server
  • btcwallet and btcd for mocking a wallet during development (optional)

Getting started

  1. Install btcd, btcwallet, and Pine lnd

  2. Start the btcd node, btcwallet, and Pine lnd node

  3. Clone this repo:

    $ git clone https://github.com/blockfirm/pine-lightning.git
    $ cd pine-lightning
    
  4. Install dependencies:

    $ npm install
    
  5. Rename src/config.template.js to src/config.js

  6. Open src/config.js and enter all settings and credentials for your nodes

  7. Create a log directory:

    $ mkdir /var/log/pine
    
  8. Start the server in development mode:

    $ npm run dev
    
  9. Or build it and run in production mode:

    $ npm run build
    $ npm start
    

Configure SSL

The Pine Payment Protocol requires all servers to be configured with SSL (HTTPS). The server doesn't directly support that so instead you need to setup a reverse proxy such as nginx and terminate there before forwarding to this server. The easiest way to obtain an SSL certificate is by using Let's Encrypt.

For information on how to proxy WebSocket traffic with nginx, see http://nginx.org/en/docs/http/websocket.html.

Client REST API

The client REST API is an API used by the Pine app to start and end websocket sessions.

Endpoints

Method Endpoint Description
POST /v1/lightning/sessions Start a new session
DELETE /v1/lightning/sessions/:sessionId End a session

POST /v1/lightning/sessions

Returns a new session ID that can be used to open a websocket connection. Requires authentication.

Returns

Returns a JSON object containing the new session ID.

Name Type Description
sessionId string A session ID that can be used to open a new websocket connection.

DELETE /v1/lightning/sessions/:sessionId

Ends an existing session. Requires authentication.

Parameters

Name Type Description
sessionId string ID of session to end.

Error handling

Errors are returned as JSON in the following format:

{
    "code": "<error code>",
    "message": "<error message>"
}

Authentication

See https://github.com/blockfirm/pine-payment-server#authentication for more information.

Rate limiting

The REST API is rate limited to 1 request per second with bursts up to 2 requests. The rate limiting is based on the Token Bucket algorithm and can be configured in src/config.js at servers.session.rateLimit.

The limit is per IP number, so if your server is behind a reverse proxy or similar you must change the config to rate limit by the X-Forwarded-For header instead of the actual IP:

rateLimit: {
  ...
  ip: false,
  xff: true
  ...
}

Client Websocket API

The client websocket API is an API used by the Pine app in order to communicate with its lnd node. Both the server and the client can make RPC calls to each other.

Requests

Requests can be sent by both the server and the client as a JSON string with the following fields:

Name Type Description
id number A unique call ID.
method string Name of method to call. Server requests: Must be one of the defined methods in rpc.proto, but starting with lowercase. Client requests: Must be one of the defined methods in methods/.
request Object Request data to pass to the method.

Responses

Responses are sent as a response to a server or client request and should have the following JSON fields:

Name Type Description
id number The call ID to respond to.
response Object Response data.

Errors should have the following format:

Name Type Description
id number The call ID to respond to.
error Object Error data.
error.name string Optional error name.
error.message string Description of the error.

Server events

The server can send sporadic events to the client that are not related to a particular RPC call. Events have the following JSON fields:

Name Type Description
event string Event name.
data Object Optional event data.

Ready event

The ready event is emitted when a client has connected and its lnd node is ready.

Event Name Event Data
ready N/A

Error event

The error event is emitted when an error occurs that is not related to a particular RPC call.

Event Name Event Data
error name and message

Ping/Pong

The server will send out a ping with a certain interval (by default every 30 seconds) to keep the connection alive and to check whether it actually is alive.

If the server sends a message containing only ping, the client must respond with pong, otherwise the server will assume the client is dead and disconnect it.

The client should assume the connection is dead if no ping has been received within the ping interval + some assumption of latency (e.g. 2 seconds).

Authentication

The websocket API requires authentication by using HTTP Basic Authorization to provide the session ID and a signature of the session ID obtained from the client REST API, signed by the same user that requested the session ID.

Set the Authorization header to the following:

Basic <credentials>

<credentials> must be replaced with a base64-encoded string of the session ID and a signature of the session ID:

base64('<sessionId>:<signature>')

The signature is a signature of the session ID using the user's private key (secp256k1.sign(sha256(sha256(sessionId)), privateKey).toBase64() with recovery).

Note: If your client doesn't support setting custom headers, use the Sec-WebSocket-Protocol header instead. This can be done by passing the header as the protocols parameter to WebSocket:

const websocket = new WebSocket(url, 'Basic <credentials>');

Rate limiting

The websocket API is rate limited for each connected client. Each client can by default send 50 messages per minute unless otherwise configured in the config file.

Node API

The node API is a gRPC API used by the customized lnd node to make requests to the Pine app through this proxy/bridge. Each RPC call will be passed on to the connected client through the client API websocket.

Documentation

For documentation of the API, please refer to the rpc.proto file.

Regenerate client

To regenerate the go client used by the Pine lnd node, run the following command:

$ npm run regenerate-proto

Testing

Unit tests

The unit tests mostly test the mock client implementation for now. The proxy and API integrations are easier to test using integration tests.

To run the unit tests, run the following command:

$ npm test

Integration tests

The integration tests test that the different APIs are working together as expected. They require that you have a testing environment set up with lnd, btcd, and btcwallet and that they are configurated to work together.

To run the integration tests, run the following command:

$ npm run test-it

Note: Since btcwallet only supports p2pkh addresses and lnd does not, you will need to apply this patch to pine-lnd before running the integration tests:

diff --git a/lnwallet/chanfunding/coin_select.go b/lnwallet/chanfunding/coin_select.go
index f1ce008d..5715ea4a 100644
--- a/lnwallet/chanfunding/coin_select.go
+++ b/lnwallet/chanfunding/coin_select.go
@@ -70,7 +70,7 @@ func CoinSelect(feeRate chainfee.SatPerKWeight, amt btcutil.Amount,

                var weightEstimate input.TxWeightEstimator

-               for _, utxo := range selectedUtxos {
+               /*for _, utxo := range selectedUtxos {
                        switch {

                        case txscript.IsPayToWitnessPubKeyHash(utxo.PkScript):
@@ -83,7 +83,7 @@ func CoinSelect(feeRate chainfee.SatPerKWeight, amt btcutil.Amount,
                                return nil, 0, fmt.Errorf("unsupported address type: %x",
                                        utxo.PkScript)
                        }
-               }
+               }*/

                // Channel funding multisig output is P2WSH.
                weightEstimate.AddP2WSHOutput()

Mocking

To facilitate development, a mock client is available that will act as a Pine client:

$ npm run mock-client

Contributing

Want to help us making Pine better? Great, but first read the CONTRIBUTING.md file for instructions.

Licensing

Pine is licensed under the Apache License, Version 2.0. See LICENSE for full license text.

About

⚡️ Bridge layer between LND and Pine.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published