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.
- Dependencies
- Getting started
- Configure SSL
- Client REST API
- Client Websocket API
- Node API
- Testing
- Contributing
- Licensing
- 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)
-
Start the btcd node, btcwallet, and Pine lnd node
-
Clone this repo:
$ git clone https://github.com/blockfirm/pine-lightning.git $ cd pine-lightning
-
Install dependencies:
$ npm install
-
Rename
src/config.template.js
tosrc/config.js
-
Open
src/config.js
and enter all settings and credentials for your nodes -
Create a log directory:
$ mkdir /var/log/pine
-
Start the server in development mode:
$ npm run dev
-
Or build it and run in production mode:
$ npm run build $ npm start
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.
The client REST API is an API used by the Pine app to start and end websocket sessions.
Method | Endpoint | Description |
---|---|---|
POST | /v1/lightning/sessions | Start a new session |
DELETE | /v1/lightning/sessions/:sessionId | End a session |
Returns a new session ID that can be used to open a websocket connection. Requires authentication.
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. |
Ends an existing session. Requires authentication.
Name | Type | Description |
---|---|---|
sessionId | string | ID of session to end. |
Errors are returned as JSON in the following format:
{
"code": "<error code>",
"message": "<error message>"
}
See https://github.com/blockfirm/pine-payment-server#authentication for more information.
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
...
}
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 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 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. |
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. |
The ready
event is emitted when a client has connected and its lnd node is ready.
Event Name | Event Data |
---|---|
ready |
N/A |
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 |
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).
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>');
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.
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.
For documentation of the API, please refer to the rpc.proto file.
To regenerate the go client used by the Pine lnd node, run the following command:
$ npm run regenerate-proto
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
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()
To facilitate development, a mock client is available that will act as a Pine client:
$ npm run mock-client
Want to help us making Pine better? Great, but first read the CONTRIBUTING.md file for instructions.
Pine is licensed under the Apache License, Version 2.0. See LICENSE for full license text.