mirror of
https://github.com/aljazceru/lspd.git
synced 2025-12-21 15:54:25 +01:00
use internal cln id as correlation id
This commit is contained in:
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
@@ -10,7 +11,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/breez/lspd/cln_plugin"
|
||||
"github.com/breez/lspd/cln_plugin/proto"
|
||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/record"
|
||||
@@ -24,7 +25,7 @@ import (
|
||||
type ClnHtlcInterceptor struct {
|
||||
pluginAddress string
|
||||
client *ClnClient
|
||||
pluginClient cln_plugin.ClnPluginClient
|
||||
pluginClient proto.ClnPluginClient
|
||||
initWg sync.WaitGroup
|
||||
doneWg sync.WaitGroup
|
||||
ctx context.Context
|
||||
@@ -51,7 +52,7 @@ func (i *ClnHtlcInterceptor) Start() error {
|
||||
return err
|
||||
}
|
||||
|
||||
i.pluginClient = cln_plugin.NewClnPluginClient(conn)
|
||||
i.pluginClient = proto.NewClnPluginClient(conn)
|
||||
i.ctx = ctx
|
||||
i.cancel = cancel
|
||||
return i.intercept()
|
||||
@@ -111,7 +112,7 @@ func (i *ClnHtlcInterceptor) intercept() error {
|
||||
request.Htlc,
|
||||
request.Onion.ShortChannelId,
|
||||
request.Htlc.AmountMsat, //with fees
|
||||
request.Onion.ForwardAmountMsat,
|
||||
request.Onion.ForwardMsat,
|
||||
request.Htlc.CltvExpiryRelative,
|
||||
request.Htlc.CltvExpiry,
|
||||
request.Htlc.PaymentHash,
|
||||
@@ -120,28 +121,25 @@ func (i *ClnHtlcInterceptor) intercept() error {
|
||||
|
||||
i.doneWg.Add(1)
|
||||
go func() {
|
||||
interceptResult := intercept(request.Htlc.PaymentHash, request.Onion.ForwardAmountMsat, request.Htlc.CltvExpiry)
|
||||
paymentHash, err := hex.DecodeString(request.Htlc.PaymentHash)
|
||||
if err != nil {
|
||||
interceptorClient.Send(i.defaultResolution(request))
|
||||
i.doneWg.Done()
|
||||
}
|
||||
interceptResult := intercept(paymentHash, request.Onion.ForwardMsat, request.Htlc.CltvExpiry)
|
||||
switch interceptResult.action {
|
||||
case INTERCEPT_RESUME_WITH_ONION:
|
||||
interceptorClient.Send(i.resumeWithOnion(request, interceptResult))
|
||||
case INTERCEPT_FAIL_HTLC_WITH_CODE:
|
||||
interceptorClient.Send(&cln_plugin.HtlcResolution{
|
||||
Correlationid: request.Correlationid,
|
||||
Outcome: &cln_plugin.HtlcResolution_Fail{
|
||||
Fail: &cln_plugin.HtlcFail{
|
||||
FailureMessage: i.mapFailureCode(interceptResult.failureCode),
|
||||
},
|
||||
},
|
||||
})
|
||||
interceptorClient.Send(
|
||||
i.failWithCode(request, interceptResult.failureCode),
|
||||
)
|
||||
case INTERCEPT_RESUME:
|
||||
fallthrough
|
||||
default:
|
||||
interceptorClient.Send(&cln_plugin.HtlcResolution{
|
||||
Correlationid: request.Correlationid,
|
||||
Outcome: &cln_plugin.HtlcResolution_Continue{
|
||||
Continue: &cln_plugin.HtlcContinue{},
|
||||
},
|
||||
})
|
||||
interceptorClient.Send(
|
||||
i.defaultResolution(request),
|
||||
)
|
||||
}
|
||||
|
||||
i.doneWg.Done()
|
||||
@@ -163,29 +161,51 @@ func (i *ClnHtlcInterceptor) WaitStarted() LightningClient {
|
||||
return i.client
|
||||
}
|
||||
|
||||
func (i *ClnHtlcInterceptor) resumeWithOnion(request *cln_plugin.HtlcAccepted, interceptResult interceptResult) *cln_plugin.HtlcResolution {
|
||||
func (i *ClnHtlcInterceptor) resumeWithOnion(request *proto.HtlcAccepted, interceptResult interceptResult) *proto.HtlcResolution {
|
||||
//decoding and encoding onion with alias in type 6 record.
|
||||
newPayload, err := encodePayloadWithNextHop(request.Onion.Payload, interceptResult.channelId)
|
||||
payload, err := hex.DecodeString(request.Onion.Payload)
|
||||
if err != nil {
|
||||
log.Printf("resumeWithOnion: hex.DecodeString(%v) error: %v", request.Onion.Payload, err)
|
||||
return i.failWithCode(request, FAILURE_TEMPORARY_CHANNEL_FAILURE)
|
||||
}
|
||||
newPayload, err := encodePayloadWithNextHop(payload, interceptResult.channelId)
|
||||
if err != nil {
|
||||
log.Printf("encodePayloadWithNextHop error: %v", err)
|
||||
return &cln_plugin.HtlcResolution{
|
||||
Correlationid: request.Correlationid,
|
||||
Outcome: &cln_plugin.HtlcResolution_Fail{
|
||||
Fail: &cln_plugin.HtlcFail{
|
||||
FailureMessage: i.mapFailureCode(FAILURE_TEMPORARY_CHANNEL_FAILURE),
|
||||
},
|
||||
},
|
||||
}
|
||||
return i.failWithCode(request, FAILURE_TEMPORARY_CHANNEL_FAILURE)
|
||||
}
|
||||
|
||||
chanId := lnwire.NewChanIDFromOutPoint(interceptResult.channelPoint)
|
||||
newPayloadStr := hex.EncodeToString(newPayload)
|
||||
|
||||
chanId := lnwire.NewChanIDFromOutPoint(interceptResult.channelPoint).String()
|
||||
log.Printf("forwarding htlc to the destination node and a new private channel was opened")
|
||||
return &cln_plugin.HtlcResolution{
|
||||
return &proto.HtlcResolution{
|
||||
Correlationid: request.Correlationid,
|
||||
Outcome: &cln_plugin.HtlcResolution_ContinueWith{
|
||||
ContinueWith: &cln_plugin.HtlcContinueWith{
|
||||
ChannelId: chanId[:],
|
||||
Payload: newPayload,
|
||||
Outcome: &proto.HtlcResolution_Continue{
|
||||
Continue: &proto.HtlcContinue{
|
||||
ForwardTo: &chanId,
|
||||
Payload: &newPayloadStr,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ClnHtlcInterceptor) defaultResolution(request *proto.HtlcAccepted) *proto.HtlcResolution {
|
||||
return &proto.HtlcResolution{
|
||||
Correlationid: request.Correlationid,
|
||||
Outcome: &proto.HtlcResolution_Continue{
|
||||
Continue: &proto.HtlcContinue{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ClnHtlcInterceptor) failWithCode(request *proto.HtlcAccepted, code interceptFailureCode) *proto.HtlcResolution {
|
||||
return &proto.HtlcResolution{
|
||||
Correlationid: request.Correlationid,
|
||||
Outcome: &proto.HtlcResolution_Fail{
|
||||
Fail: &proto.HtlcFail{
|
||||
Failure: &proto.HtlcFail_FailureMessage{
|
||||
FailureMessage: i.mapFailureCode(code),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -237,16 +257,16 @@ func encodePayloadWithNextHop(payload []byte, channelId uint64) ([]byte, error)
|
||||
return newPayloadBuf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (i *ClnHtlcInterceptor) mapFailureCode(original interceptFailureCode) []byte {
|
||||
func (i *ClnHtlcInterceptor) mapFailureCode(original interceptFailureCode) string {
|
||||
switch original {
|
||||
case FAILURE_TEMPORARY_CHANNEL_FAILURE:
|
||||
return []byte{0x10, 0x07}
|
||||
return "1007"
|
||||
case FAILURE_TEMPORARY_NODE_FAILURE:
|
||||
return []byte{0x20, 0x02}
|
||||
return "2002"
|
||||
case FAILURE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS:
|
||||
return []byte{0x40, 0x0F}
|
||||
return "400F"
|
||||
default:
|
||||
log.Printf("Unknown failure code %v, default to temporary channel failure.", original)
|
||||
return []byte{0x10, 0x07} // temporary channel failure
|
||||
return "1007" // temporary channel failure
|
||||
}
|
||||
}
|
||||
|
||||
117
cln_plugin/cln_messages.go
Normal file
117
cln_plugin/cln_messages.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package cln_plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Id json.RawMessage `json:"id,omitempty"`
|
||||
Method string `json:"method"`
|
||||
JsonRpc string `json:"jsonrpc"`
|
||||
Params json.RawMessage `json:"params,omitempty"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Id json.RawMessage `json:"id"`
|
||||
JsonRpc string `json:"jsonrpc"`
|
||||
Result Result `json:"result,omitempty"`
|
||||
Error *RpcError `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type Result interface{}
|
||||
|
||||
type RpcError struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data json.RawMessage `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
type Manifest struct {
|
||||
Options []Option `json:"options"`
|
||||
RpcMethods []*RpcMethod `json:"rpcmethods"`
|
||||
Dynamic bool `json:"dynamic"`
|
||||
Subscriptions []string `json:"subscriptions,omitempty"`
|
||||
Hooks []Hook `json:"hooks,omitempty"`
|
||||
FeatureBits *FeatureBits `json:"featurebits,omitempty"`
|
||||
NonNumericIds bool `json:"nonnumericids"`
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Default *string `json:"default,omitempty"`
|
||||
Multi *bool `json:"multi,omitempty"`
|
||||
Deprecated *bool `json:"deprecated,omitempty"`
|
||||
}
|
||||
|
||||
type RpcMethod struct {
|
||||
Name string `json:"name"`
|
||||
Usage string `json:"usage"`
|
||||
Description string `json:"description"`
|
||||
LongDescription *string `json:"long_description,omitempty"`
|
||||
Deprecated *bool `json:"deprecated,omitempty"`
|
||||
}
|
||||
|
||||
type Hook struct {
|
||||
Name string `json:"name"`
|
||||
Before []string `json:"before,omitempty"`
|
||||
}
|
||||
|
||||
type FeatureBits struct {
|
||||
Node *string `json:"node,omitempty"`
|
||||
Init *string `json:"init,omitempty"`
|
||||
Channel *string `json:"channel,omitempty"`
|
||||
Invoice *string `json:"invoice,omitempty"`
|
||||
}
|
||||
|
||||
type InitMessage struct {
|
||||
Options map[string]interface{} `json:"options,omitempty"`
|
||||
Configuration *InitConfiguration `json:"configuration,omitempty"`
|
||||
}
|
||||
|
||||
type InitConfiguration struct {
|
||||
LightningDir string `json:"lightning-dir"`
|
||||
RpcFile string `json:"rpc-file"`
|
||||
Startup bool `json:"startup"`
|
||||
Network string `json:"network"`
|
||||
FeatureSet *FeatureBits `json:"feature_set"`
|
||||
Proxy *Proxy `json:"proxy"`
|
||||
TorV3Enabled bool `json:"torv3-enabled"`
|
||||
AlwaysUseProxy bool `json:"always_use_proxy"`
|
||||
}
|
||||
|
||||
type Proxy struct {
|
||||
Type string `json:"type"`
|
||||
Address string `json:"address"`
|
||||
Port int `json:"port"`
|
||||
}
|
||||
|
||||
type HtlcAccepted struct {
|
||||
Onion *Onion `json:"onion"`
|
||||
Htlc *Htlc `json:"htlc"`
|
||||
ForwardTo string `json:"forward_to"`
|
||||
}
|
||||
|
||||
type Onion struct {
|
||||
Payload string `json:"payload"`
|
||||
ShortChannelId string `json:"short_channel_id"`
|
||||
ForwardMsat uint64 `json:"forward_msat"`
|
||||
OutgoingCltvValue uint32 `json:"outgoing_cltv_value"`
|
||||
SharedSecret string `json:"shared_secret"`
|
||||
NextOnion string `json:"next_onion"`
|
||||
}
|
||||
|
||||
type Htlc struct {
|
||||
ShortChannelId string `json:"short_channel_id"`
|
||||
Id uint64 `json:"id"`
|
||||
AmountMsat uint64 `json:"amount_msat"`
|
||||
CltvExpiry uint32 `json:"cltv_expiry"`
|
||||
CltvExpiryRelative uint32 `json:"cltv_expiry_relative"`
|
||||
PaymentHash string `json:"payment_hash"`
|
||||
}
|
||||
|
||||
type LogNotification struct {
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
@@ -1,110 +1,488 @@
|
||||
package cln_plugin
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
"github.com/breez/lspd/basetypes"
|
||||
"github.com/niftynei/glightning/glightning"
|
||||
const (
|
||||
SubscriberTimeoutOption = "lsp.subscribertimeout"
|
||||
ListenAddressOption = "lsp.listen"
|
||||
)
|
||||
|
||||
var (
|
||||
DefaultSubscriberTimeout = "1m"
|
||||
)
|
||||
|
||||
const (
|
||||
MaxIntakeBuffer = 500 * 1024 * 1023
|
||||
)
|
||||
|
||||
const (
|
||||
SpecVersion = "2.0"
|
||||
ParseError = -32700
|
||||
InvalidRequest = -32600
|
||||
MethodNotFound = -32601
|
||||
InvalidParams = -32603
|
||||
InternalErr = -32603
|
||||
)
|
||||
|
||||
var (
|
||||
TwoNewLines = []byte("\n\n")
|
||||
)
|
||||
|
||||
type ClnPlugin struct {
|
||||
done chan struct{}
|
||||
server *server
|
||||
plugin *glightning.Plugin
|
||||
in *os.File
|
||||
out *bufio.Writer
|
||||
writeMtx sync.Mutex
|
||||
}
|
||||
|
||||
func NewClnPlugin(server *server) *ClnPlugin {
|
||||
func NewClnPlugin(in, out *os.File) *ClnPlugin {
|
||||
c := &ClnPlugin{
|
||||
server: server,
|
||||
done: make(chan struct{}),
|
||||
in: in,
|
||||
out: bufio.NewWriter(out),
|
||||
}
|
||||
|
||||
c.plugin = glightning.NewPlugin(c.onInit)
|
||||
c.plugin.RegisterHooks(&glightning.Hooks{
|
||||
HtlcAccepted: c.onHtlcAccepted,
|
||||
})
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClnPlugin) Start() error {
|
||||
err := c.plugin.Start(os.Stdin, os.Stdout)
|
||||
if err != nil {
|
||||
log.Printf("Plugin error: %v", err)
|
||||
// Starts the cln plugin.
|
||||
// NOTE: The grpc server is started in the handleInit function.
|
||||
func (c *ClnPlugin) Start() {
|
||||
c.setupLogging()
|
||||
go c.listen()
|
||||
<-c.done
|
||||
}
|
||||
|
||||
func (c *ClnPlugin) setupLogging() {
|
||||
in, out := io.Pipe()
|
||||
go func(in io.Reader) {
|
||||
// everytime we get a new message, log it thru c-lightning
|
||||
scanner := bufio.NewScanner(in)
|
||||
for {
|
||||
select {
|
||||
case <-c.done:
|
||||
return
|
||||
default:
|
||||
if !scanner.Scan() {
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatalf(
|
||||
"can't print out to std err, killing: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range strings.Split(scanner.Text(), "\n") {
|
||||
c.log("info", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}(in)
|
||||
log.SetFlags(log.Ltime | log.Lshortfile)
|
||||
log.SetOutput(out)
|
||||
}
|
||||
|
||||
// Stops the cln plugin. Drops any remaining work immediately.
|
||||
// Pending htlcs will be replayed when cln starts again.
|
||||
func (c *ClnPlugin) Stop() {
|
||||
close(c.done)
|
||||
|
||||
s := c.server
|
||||
if s != nil {
|
||||
s.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
// listens stdout for requests from cln and sends the requests to the
|
||||
// appropriate handler in fifo order.
|
||||
func (c *ClnPlugin) listen() error {
|
||||
scanner := bufio.NewScanner(c.in)
|
||||
buf := make([]byte, 1024)
|
||||
scanner.Buffer(buf, MaxIntakeBuffer)
|
||||
|
||||
// cln messages are split by a double newline.
|
||||
scanner.Split(scanDoubleNewline)
|
||||
for {
|
||||
select {
|
||||
case <-c.done:
|
||||
return nil
|
||||
default:
|
||||
if !scanner.Scan() {
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := scanner.Bytes()
|
||||
// TODO: Pipe logs to the proper place.
|
||||
log.Println(string(msg))
|
||||
// pass down a copy so things stay sane
|
||||
msg_buf := make([]byte, len(msg))
|
||||
copy(msg_buf, msg)
|
||||
|
||||
// NOTE: processMsg is synchronous, so it should only do quick work.
|
||||
c.processMsg(msg_buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClnPlugin) Stop() {
|
||||
c.plugin.Stop()
|
||||
c.server.Stop()
|
||||
// processes a single message from cln. Sends the message to the appropriate
|
||||
// handler.
|
||||
func (c *ClnPlugin) processMsg(msg []byte) {
|
||||
if len(msg) == 0 {
|
||||
c.sendError(nil, InvalidRequest, "Invalid Request")
|
||||
return
|
||||
}
|
||||
|
||||
// Right now we don't handle arrays of requests...
|
||||
if msg[0] == '[' {
|
||||
var requests []*Request
|
||||
err := json.Unmarshal(msg, &requests)
|
||||
if err != nil {
|
||||
c.sendError(
|
||||
nil,
|
||||
ParseError,
|
||||
fmt.Sprintf("Parse error:%s [%s]", err.Error(), msg),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
for _, request := range requests {
|
||||
c.processRequest(request)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the received buffer into a request object.
|
||||
var request Request
|
||||
err := json.Unmarshal(msg, &request)
|
||||
if err != nil {
|
||||
log.Printf("failed to unmarshal request: %v", err)
|
||||
c.sendError(
|
||||
nil,
|
||||
ParseError,
|
||||
fmt.Sprintf("Parse error:%s [%s]", err.Error(), msg),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
c.processRequest(&request)
|
||||
}
|
||||
|
||||
func (c *ClnPlugin) onInit(plugin *glightning.Plugin, options map[string]glightning.Option, config *glightning.Config) {
|
||||
log.Printf("successfully init'd! %v\n", config.RpcFile)
|
||||
|
||||
log.Printf("Starting htlc grpc server.")
|
||||
go func() {
|
||||
err := c.server.Start()
|
||||
if err == nil {
|
||||
log.Printf("WARNING server stopped.")
|
||||
} else {
|
||||
log.Printf("ERROR Server stopped with error: %v", err)
|
||||
func (c *ClnPlugin) processRequest(request *Request) {
|
||||
// Make sure the spec version is expected.
|
||||
if request.JsonRpc != SpecVersion {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
InvalidRequest,
|
||||
fmt.Sprintf(
|
||||
`Invalid jsonrpc, expected '%s' got '%s'`,
|
||||
SpecVersion,
|
||||
request.JsonRpc,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Send the message to the appropriate handler.
|
||||
switch request.Method {
|
||||
case "getmanifest":
|
||||
c.handleGetManifest(request)
|
||||
case "init":
|
||||
c.handleInit(request)
|
||||
case "shutdown":
|
||||
c.handleShutdown(request)
|
||||
case "htlc_accepted":
|
||||
c.handleHtlcAccepted(request)
|
||||
default:
|
||||
c.sendError(
|
||||
request.Id,
|
||||
MethodNotFound,
|
||||
fmt.Sprintf("Method '%s' not found", request.Method),
|
||||
)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *ClnPlugin) onHtlcAccepted(event *glightning.HtlcAcceptedEvent) (*glightning.HtlcAcceptedResponse, error) {
|
||||
payload, err := hex.DecodeString(event.Onion.Payload)
|
||||
if err != nil {
|
||||
log.Printf("ERROR failed to decode payload %s: %v", event.Onion.Payload, err)
|
||||
return nil, err
|
||||
}
|
||||
scid, err := basetypes.NewShortChannelIDFromString(event.Onion.ShortChannelId)
|
||||
if err != nil {
|
||||
log.Printf("ERROR failed to decode short channel id %s: %v", event.Onion.ShortChannelId, err)
|
||||
return nil, err
|
||||
}
|
||||
ph, err := hex.DecodeString(event.Htlc.PaymentHash)
|
||||
if err != nil {
|
||||
log.Printf("ERROR failed to decode payment hash %s: %v", event.Onion.ShortChannelId, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := c.server.Send(&HtlcAccepted{
|
||||
Onion: &Onion{
|
||||
Payload: payload,
|
||||
ShortChannelId: uint64(*scid),
|
||||
ForwardAmountMsat: event.Onion.ForwardAmount,
|
||||
// Returns this plugin's manifest to cln.
|
||||
func (c *ClnPlugin) handleGetManifest(request *Request) {
|
||||
c.sendToCln(&Response{
|
||||
Id: request.Id,
|
||||
JsonRpc: SpecVersion,
|
||||
Result: &Manifest{
|
||||
Options: []Option{
|
||||
{
|
||||
Name: ListenAddressOption,
|
||||
Type: "string",
|
||||
Description: "listen address for the htlc_accepted lsp " +
|
||||
"grpc server",
|
||||
},
|
||||
{
|
||||
Name: SubscriberTimeoutOption,
|
||||
Type: "string",
|
||||
Description: "htlc timeout duration when there is no " +
|
||||
"subscriber to the grpc server. golang duration " +
|
||||
"string.",
|
||||
Default: &DefaultSubscriberTimeout,
|
||||
},
|
||||
},
|
||||
RpcMethods: []*RpcMethod{},
|
||||
Dynamic: true,
|
||||
Hooks: []Hook{
|
||||
{
|
||||
Name: "htlc_accepted",
|
||||
},
|
||||
},
|
||||
NonNumericIds: true,
|
||||
Subscriptions: []string{
|
||||
"shutdown",
|
||||
},
|
||||
Htlc: &HtlcOffer{
|
||||
AmountMsat: event.Htlc.AmountMilliSatoshi,
|
||||
CltvExpiryRelative: uint32(event.Htlc.CltvExpiryRelative),
|
||||
CltvExpiry: uint32(event.Htlc.CltvExpiry),
|
||||
PaymentHash: ph,
|
||||
},
|
||||
})
|
||||
|
||||
_, ok := resp.Outcome.(*HtlcResolution_Continue)
|
||||
if ok {
|
||||
return event.Continue(), nil
|
||||
}
|
||||
|
||||
cont, ok := resp.Outcome.(*HtlcResolution_ContinueWith)
|
||||
if ok {
|
||||
chanId := hex.EncodeToString(cont.ContinueWith.ChannelId)
|
||||
pl := hex.EncodeToString(cont.ContinueWith.Payload)
|
||||
return event.ContinueWith(chanId, pl), nil
|
||||
}
|
||||
|
||||
fail, ok := resp.Outcome.(*HtlcResolution_Fail)
|
||||
if ok {
|
||||
fm := hex.EncodeToString(fail.Fail.FailureMessage)
|
||||
return event.Fail(fm), err
|
||||
}
|
||||
|
||||
log.Printf("Unexpected htlc resolution type %T: %+v", resp.Outcome, resp.Outcome)
|
||||
return event.Fail("1007"), nil // temporary channel failure
|
||||
}
|
||||
|
||||
// Handles plugin initialization. Parses startup options and starts the grpc
|
||||
// server.
|
||||
func (c *ClnPlugin) handleInit(request *Request) {
|
||||
// Deserialize the init message.
|
||||
var initMsg InitMessage
|
||||
err := json.Unmarshal(request.Params, &initMsg)
|
||||
if err != nil {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
ParseError,
|
||||
fmt.Sprintf(
|
||||
"Error parsing init params:%s [%s]",
|
||||
err.Error(),
|
||||
request.Params,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the listen address option.
|
||||
l, ok := initMsg.Options[ListenAddressOption]
|
||||
if !ok {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
InvalidParams,
|
||||
fmt.Sprintf("Missing option '%s'", ListenAddressOption),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
addr, ok := l.(string)
|
||||
if !ok || addr == "" {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
InvalidParams,
|
||||
fmt.Sprintf(
|
||||
"Invalid value '%v' for option '%s'",
|
||||
l,
|
||||
ListenAddressOption,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the subscriber timeout option.
|
||||
t, ok := initMsg.Options[SubscriberTimeoutOption]
|
||||
if !ok {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
InvalidParams,
|
||||
fmt.Sprintf("Missing option '%s'", SubscriberTimeoutOption),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
s, ok := t.(string)
|
||||
if !ok || s == "" {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
InvalidParams,
|
||||
fmt.Sprintf(
|
||||
"Invalid value '%v' for option '%s'",
|
||||
t,
|
||||
SubscriberTimeoutOption,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
subscriberTimeout, err := time.ParseDuration(s)
|
||||
if err != nil {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
InvalidParams,
|
||||
fmt.Sprintf(
|
||||
"Invalid value '%v' for option '%s'",
|
||||
s,
|
||||
SubscriberTimeoutOption,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Start the grpc server.
|
||||
c.server = NewServer(addr, subscriberTimeout)
|
||||
go c.server.Start()
|
||||
err = c.server.WaitStarted()
|
||||
if err != nil {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
InternalErr,
|
||||
fmt.Sprintf("Failed to start server: %s", err.Error()),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Listen for responses from the grpc server.
|
||||
go c.listenServer()
|
||||
|
||||
// Let cln know the plugin is initialized.
|
||||
c.sendToCln(&Response{
|
||||
Id: request.Id,
|
||||
JsonRpc: SpecVersion,
|
||||
})
|
||||
}
|
||||
|
||||
// Listens to responses to htlc_accepted requests from the grpc server.
|
||||
func (c *ClnPlugin) listenServer() {
|
||||
for {
|
||||
select {
|
||||
case <-c.done:
|
||||
return
|
||||
default:
|
||||
id, result := c.server.Receive()
|
||||
|
||||
// The server may return nil if it is stopped.
|
||||
if result == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
serid, _ := json.Marshal(&id)
|
||||
c.sendToCln(&Response{
|
||||
Id: serid,
|
||||
JsonRpc: SpecVersion,
|
||||
Result: result,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles the shutdown message. Stops any work immediately.
|
||||
func (c *ClnPlugin) handleShutdown(request *Request) {
|
||||
c.Stop()
|
||||
}
|
||||
|
||||
// Sends a htlc_accepted message to the grpc server.
|
||||
func (c *ClnPlugin) handleHtlcAccepted(request *Request) {
|
||||
var htlc HtlcAccepted
|
||||
err := json.Unmarshal(request.Params, &htlc)
|
||||
if err != nil {
|
||||
c.sendError(
|
||||
request.Id,
|
||||
ParseError,
|
||||
fmt.Sprintf(
|
||||
"Error parsing htlc_accepted params:%s [%s]",
|
||||
err.Error(),
|
||||
request.Params,
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
c.server.Send(c.idToString(request.Id), &htlc)
|
||||
}
|
||||
|
||||
// Sends an error to cln.
|
||||
func (c *ClnPlugin) sendError(id json.RawMessage, code int, message string) {
|
||||
resp := &Response{
|
||||
JsonRpc: SpecVersion,
|
||||
Error: &RpcError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
},
|
||||
}
|
||||
|
||||
if len(id) > 0 {
|
||||
resp.Id = id
|
||||
}
|
||||
|
||||
c.sendToCln(resp)
|
||||
}
|
||||
|
||||
// converts a raw cln id to string. The CLN id can either be an integer or a
|
||||
// string. if it's a string, the quotes are removed.
|
||||
func (c *ClnPlugin) idToString(id json.RawMessage) string {
|
||||
if len(id) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
str := string(id)
|
||||
str = strings.TrimSpace(str)
|
||||
str = strings.Trim(str, "\"")
|
||||
str = strings.Trim(str, "'")
|
||||
return str
|
||||
}
|
||||
|
||||
// Sends a message to cln.
|
||||
func (c *ClnPlugin) sendToCln(msg interface{}) {
|
||||
c.writeMtx.Lock()
|
||||
defer c.writeMtx.Unlock()
|
||||
|
||||
// TODO: log
|
||||
data, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
data = append(data, TwoNewLines...)
|
||||
c.out.Write(data)
|
||||
c.out.Flush()
|
||||
}
|
||||
|
||||
func (c *ClnPlugin) log(level string, message string) {
|
||||
params, _ := json.Marshal(&LogNotification{
|
||||
Level: level,
|
||||
Message: message,
|
||||
})
|
||||
|
||||
c.sendToCln(&Request{
|
||||
Method: "log",
|
||||
JsonRpc: SpecVersion,
|
||||
Params: params,
|
||||
})
|
||||
}
|
||||
|
||||
// Helper method for the bufio scanner to split messages on double newlines.
|
||||
func scanDoubleNewline(
|
||||
data []byte,
|
||||
atEOF bool,
|
||||
) (advance int, token []byte, err error) {
|
||||
for i := 0; i < len(data); i++ {
|
||||
if data[i] == '\n' && (i+1) < len(data) && data[i+1] == '\n' {
|
||||
return i + 2, data[:i], nil
|
||||
}
|
||||
}
|
||||
// this trashes anything left over in
|
||||
// the buffer if we're at EOF, with no /n/n present
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
@@ -1,674 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.8
|
||||
// source: cln_plugin.proto
|
||||
|
||||
package cln_plugin
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type HtlcAccepted struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Correlationid uint64 `protobuf:"varint,1,opt,name=correlationid,proto3" json:"correlationid,omitempty"`
|
||||
Onion *Onion `protobuf:"bytes,2,opt,name=onion,proto3" json:"onion,omitempty"`
|
||||
Htlc *HtlcOffer `protobuf:"bytes,3,opt,name=htlc,proto3" json:"htlc,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) Reset() {
|
||||
*x = HtlcAccepted{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcAccepted) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcAccepted) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcAccepted.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcAccepted) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) GetCorrelationid() uint64 {
|
||||
if x != nil {
|
||||
return x.Correlationid
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) GetOnion() *Onion {
|
||||
if x != nil {
|
||||
return x.Onion
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) GetHtlc() *HtlcOffer {
|
||||
if x != nil {
|
||||
return x.Htlc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Onion struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
ShortChannelId uint64 `protobuf:"varint,2,opt,name=short_channel_id,json=shortChannelId,proto3" json:"short_channel_id,omitempty"`
|
||||
ForwardAmountMsat uint64 `protobuf:"varint,3,opt,name=forward_amount_msat,json=forwardAmountMsat,proto3" json:"forward_amount_msat,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Onion) Reset() {
|
||||
*x = Onion{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Onion) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Onion) ProtoMessage() {}
|
||||
|
||||
func (x *Onion) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Onion.ProtoReflect.Descriptor instead.
|
||||
func (*Onion) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Onion) GetPayload() []byte {
|
||||
if x != nil {
|
||||
return x.Payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Onion) GetShortChannelId() uint64 {
|
||||
if x != nil {
|
||||
return x.ShortChannelId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Onion) GetForwardAmountMsat() uint64 {
|
||||
if x != nil {
|
||||
return x.ForwardAmountMsat
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type HtlcOffer struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
AmountMsat uint64 `protobuf:"varint,2,opt,name=amount_msat,json=amountMsat,proto3" json:"amount_msat,omitempty"`
|
||||
CltvExpiryRelative uint32 `protobuf:"varint,3,opt,name=cltv_expiry_relative,json=cltvExpiryRelative,proto3" json:"cltv_expiry_relative,omitempty"`
|
||||
CltvExpiry uint32 `protobuf:"varint,4,opt,name=cltv_expiry,json=cltvExpiry,proto3" json:"cltv_expiry,omitempty"`
|
||||
PaymentHash []byte `protobuf:"bytes,5,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HtlcOffer) Reset() {
|
||||
*x = HtlcOffer{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcOffer) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcOffer) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcOffer) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcOffer.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcOffer) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *HtlcOffer) GetAmountMsat() uint64 {
|
||||
if x != nil {
|
||||
return x.AmountMsat
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HtlcOffer) GetCltvExpiryRelative() uint32 {
|
||||
if x != nil {
|
||||
return x.CltvExpiryRelative
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HtlcOffer) GetCltvExpiry() uint32 {
|
||||
if x != nil {
|
||||
return x.CltvExpiry
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *HtlcOffer) GetPaymentHash() []byte {
|
||||
if x != nil {
|
||||
return x.PaymentHash
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type HtlcResolution struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Correlationid uint64 `protobuf:"varint,1,opt,name=correlationid,proto3" json:"correlationid,omitempty"`
|
||||
// Types that are assignable to Outcome:
|
||||
// *HtlcResolution_Fail
|
||||
// *HtlcResolution_Continue
|
||||
// *HtlcResolution_ContinueWith
|
||||
Outcome isHtlcResolution_Outcome `protobuf_oneof:"outcome"`
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) Reset() {
|
||||
*x = HtlcResolution{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcResolution) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcResolution) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcResolution.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcResolution) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) GetCorrelationid() uint64 {
|
||||
if x != nil {
|
||||
return x.Correlationid
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *HtlcResolution) GetOutcome() isHtlcResolution_Outcome {
|
||||
if m != nil {
|
||||
return m.Outcome
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) GetFail() *HtlcFail {
|
||||
if x, ok := x.GetOutcome().(*HtlcResolution_Fail); ok {
|
||||
return x.Fail
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) GetContinue() *HtlcContinue {
|
||||
if x, ok := x.GetOutcome().(*HtlcResolution_Continue); ok {
|
||||
return x.Continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) GetContinueWith() *HtlcContinueWith {
|
||||
if x, ok := x.GetOutcome().(*HtlcResolution_ContinueWith); ok {
|
||||
return x.ContinueWith
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isHtlcResolution_Outcome interface {
|
||||
isHtlcResolution_Outcome()
|
||||
}
|
||||
|
||||
type HtlcResolution_Fail struct {
|
||||
Fail *HtlcFail `protobuf:"bytes,2,opt,name=fail,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HtlcResolution_Continue struct {
|
||||
Continue *HtlcContinue `protobuf:"bytes,3,opt,name=continue,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HtlcResolution_ContinueWith struct {
|
||||
ContinueWith *HtlcContinueWith `protobuf:"bytes,4,opt,name=continue_with,json=continueWith,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*HtlcResolution_Fail) isHtlcResolution_Outcome() {}
|
||||
|
||||
func (*HtlcResolution_Continue) isHtlcResolution_Outcome() {}
|
||||
|
||||
func (*HtlcResolution_ContinueWith) isHtlcResolution_Outcome() {}
|
||||
|
||||
type HtlcFail struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
FailureMessage []byte `protobuf:"bytes,1,opt,name=failure_message,json=failureMessage,proto3" json:"failure_message,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HtlcFail) Reset() {
|
||||
*x = HtlcFail{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcFail) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcFail) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcFail) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcFail.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcFail) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *HtlcFail) GetFailureMessage() []byte {
|
||||
if x != nil {
|
||||
return x.FailureMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type HtlcContinue struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *HtlcContinue) Reset() {
|
||||
*x = HtlcContinue{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcContinue) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcContinue) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcContinue) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcContinue.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcContinue) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
type HtlcContinueWith struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ChannelId []byte `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"`
|
||||
Payload []byte `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HtlcContinueWith) Reset() {
|
||||
*x = HtlcContinueWith{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcContinueWith) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcContinueWith) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcContinueWith) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcContinueWith.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcContinueWith) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *HtlcContinueWith) GetChannelId() []byte {
|
||||
if x != nil {
|
||||
return x.ChannelId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcContinueWith) GetPayload() []byte {
|
||||
if x != nil {
|
||||
return x.Payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_cln_plugin_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_cln_plugin_proto_rawDesc = []byte{
|
||||
0x0a, 0x10, 0x63, 0x6c, 0x6e, 0x5f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x22, 0x72, 0x0a, 0x0c, 0x48, 0x74, 0x6c, 0x63, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74,
|
||||
0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65,
|
||||
0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x05, 0x6f, 0x6e, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x4f, 0x6e, 0x69, 0x6f, 0x6e, 0x52,
|
||||
0x05, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x04, 0x68, 0x74, 0x6c, 0x63, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x4f, 0x66, 0x66, 0x65, 0x72,
|
||||
0x52, 0x04, 0x68, 0x74, 0x6c, 0x63, 0x22, 0x7b, 0x0a, 0x05, 0x4f, 0x6e, 0x69, 0x6f, 0x6e, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x68, 0x6f,
|
||||
0x72, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
|
||||
0x6c, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x61,
|
||||
0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x11, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d,
|
||||
0x73, 0x61, 0x74, 0x22, 0xa2, 0x01, 0x0a, 0x09, 0x48, 0x74, 0x6c, 0x63, 0x4f, 0x66, 0x66, 0x65,
|
||||
0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73,
|
||||
0x61, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72,
|
||||
0x79, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x12, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x52, 0x65, 0x6c, 0x61,
|
||||
0x74, 0x69, 0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70,
|
||||
0x69, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45,
|
||||
0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x70, 0x61, 0x79,
|
||||
0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68, 0x22, 0xc9, 0x01, 0x0a, 0x0e, 0x48, 0x74, 0x6c,
|
||||
0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x63,
|
||||
0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x04, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x69,
|
||||
0x64, 0x12, 0x1f, 0x0a, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x09, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x46, 0x61, 0x69, 0x6c, 0x48, 0x00, 0x52, 0x04, 0x66, 0x61,
|
||||
0x69, 0x6c, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x69,
|
||||
0x6e, 0x75, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x12,
|
||||
0x38, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x5f, 0x77, 0x69, 0x74, 0x68,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e,
|
||||
0x74, 0x69, 0x6e, 0x75, 0x65, 0x57, 0x69, 0x74, 0x68, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6f, 0x6e,
|
||||
0x74, 0x69, 0x6e, 0x75, 0x65, 0x57, 0x69, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x6f, 0x75, 0x74,
|
||||
0x63, 0x6f, 0x6d, 0x65, 0x22, 0x33, 0x0a, 0x08, 0x48, 0x74, 0x6c, 0x63, 0x46, 0x61, 0x69, 0x6c,
|
||||
0x12, 0x27, 0x0a, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75,
|
||||
0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x0e, 0x0a, 0x0c, 0x48, 0x74, 0x6c,
|
||||
0x63, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x22, 0x4b, 0x0a, 0x10, 0x48, 0x74, 0x6c,
|
||||
0x63, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x57, 0x69, 0x74, 0x68, 0x12, 0x1d, 0x0a,
|
||||
0x0a, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x09, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07,
|
||||
0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70,
|
||||
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x32, 0x3d, 0x0a, 0x09, 0x43, 0x6c, 0x6e, 0x50, 0x6c, 0x75,
|
||||
0x67, 0x69, 0x6e, 0x12, 0x30, 0x0a, 0x0a, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x74, 0x72, 0x65, 0x61,
|
||||
0x6d, 0x12, 0x0f, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x1a, 0x0d, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65,
|
||||
0x64, 0x28, 0x01, 0x30, 0x01, 0x42, 0x22, 0x5a, 0x20, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x72, 0x65, 0x65, 0x7a, 0x2f, 0x6c, 0x73, 0x70, 0x64, 0x2f, 0x63,
|
||||
0x6c, 0x6e, 0x5f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_cln_plugin_proto_rawDescOnce sync.Once
|
||||
file_cln_plugin_proto_rawDescData = file_cln_plugin_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_cln_plugin_proto_rawDescGZIP() []byte {
|
||||
file_cln_plugin_proto_rawDescOnce.Do(func() {
|
||||
file_cln_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_cln_plugin_proto_rawDescData)
|
||||
})
|
||||
return file_cln_plugin_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_cln_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_cln_plugin_proto_goTypes = []interface{}{
|
||||
(*HtlcAccepted)(nil), // 0: HtlcAccepted
|
||||
(*Onion)(nil), // 1: Onion
|
||||
(*HtlcOffer)(nil), // 2: HtlcOffer
|
||||
(*HtlcResolution)(nil), // 3: HtlcResolution
|
||||
(*HtlcFail)(nil), // 4: HtlcFail
|
||||
(*HtlcContinue)(nil), // 5: HtlcContinue
|
||||
(*HtlcContinueWith)(nil), // 6: HtlcContinueWith
|
||||
}
|
||||
var file_cln_plugin_proto_depIdxs = []int32{
|
||||
1, // 0: HtlcAccepted.onion:type_name -> Onion
|
||||
2, // 1: HtlcAccepted.htlc:type_name -> HtlcOffer
|
||||
4, // 2: HtlcResolution.fail:type_name -> HtlcFail
|
||||
5, // 3: HtlcResolution.continue:type_name -> HtlcContinue
|
||||
6, // 4: HtlcResolution.continue_with:type_name -> HtlcContinueWith
|
||||
3, // 5: ClnPlugin.HtlcStream:input_type -> HtlcResolution
|
||||
0, // 6: ClnPlugin.HtlcStream:output_type -> HtlcAccepted
|
||||
6, // [6:7] is the sub-list for method output_type
|
||||
5, // [5:6] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension type_name
|
||||
5, // [5:5] is the sub-list for extension extendee
|
||||
0, // [0:5] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_cln_plugin_proto_init() }
|
||||
func file_cln_plugin_proto_init() {
|
||||
if File_cln_plugin_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_cln_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcAccepted); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Onion); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcOffer); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcResolution); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcFail); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcContinue); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcContinueWith); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[3].OneofWrappers = []interface{}{
|
||||
(*HtlcResolution_Fail)(nil),
|
||||
(*HtlcResolution_Continue)(nil),
|
||||
(*HtlcResolution_ContinueWith)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_cln_plugin_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_cln_plugin_proto_goTypes,
|
||||
DependencyIndexes: file_cln_plugin_proto_depIdxs,
|
||||
MessageInfos: file_cln_plugin_proto_msgTypes,
|
||||
}.Build()
|
||||
File_cln_plugin_proto = out.File
|
||||
file_cln_plugin_proto_rawDesc = nil
|
||||
file_cln_plugin_proto_goTypes = nil
|
||||
file_cln_plugin_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,31 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/breez/lspd/cln_plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
listen := os.Getenv("LISTEN_ADDRESS")
|
||||
server := cln_plugin.NewServer(listen)
|
||||
plugin := cln_plugin.NewClnPlugin(server)
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGINT)
|
||||
go func() {
|
||||
sig := <-c
|
||||
log.Printf("Received stop signal %v. Stopping.", sig)
|
||||
plugin.Stop()
|
||||
}()
|
||||
|
||||
err := plugin.Start()
|
||||
if err == nil {
|
||||
log.Printf("cln plugin stopped.")
|
||||
} else {
|
||||
log.Printf("cln plugin stopped with error: %v", err)
|
||||
}
|
||||
plugin := cln_plugin.NewClnPlugin(os.Stdin, os.Stdout)
|
||||
plugin.Start()
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
SCRIPTDIR=$(dirname $0)
|
||||
PROTO_ROOT=$SCRIPTDIR/proto
|
||||
|
||||
protoc --go_out=$SCRIPTDIR --go_opt=paths=source_relative --go-grpc_out=$SCRIPTDIR --go-grpc_opt=paths=source_relative -I=$PROTO_ROOT $PROTO_ROOT/*
|
||||
protoc --go_out=$PROTO_ROOT --go_opt=paths=source_relative --go-grpc_out=$PROTO_ROOT --go-grpc_opt=paths=source_relative -I=$PROTO_ROOT $PROTO_ROOT/*.proto
|
||||
787
cln_plugin/proto/cln_plugin.pb.go
Normal file
787
cln_plugin/proto/cln_plugin.pb.go
Normal file
@@ -0,0 +1,787 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.28.1
|
||||
// protoc v3.21.12
|
||||
// source: cln_plugin.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type HtlcAccepted struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Correlationid string `protobuf:"bytes,1,opt,name=correlationid,proto3" json:"correlationid,omitempty"`
|
||||
Onion *Onion `protobuf:"bytes,2,opt,name=onion,proto3" json:"onion,omitempty"`
|
||||
Htlc *Htlc `protobuf:"bytes,3,opt,name=htlc,proto3" json:"htlc,omitempty"`
|
||||
ForwardTo string `protobuf:"bytes,4,opt,name=forward_to,json=forwardTo,proto3" json:"forward_to,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) Reset() {
|
||||
*x = HtlcAccepted{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcAccepted) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcAccepted) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcAccepted.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcAccepted) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) GetCorrelationid() string {
|
||||
if x != nil {
|
||||
return x.Correlationid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) GetOnion() *Onion {
|
||||
if x != nil {
|
||||
return x.Onion
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) GetHtlc() *Htlc {
|
||||
if x != nil {
|
||||
return x.Htlc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcAccepted) GetForwardTo() string {
|
||||
if x != nil {
|
||||
return x.ForwardTo
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Onion struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Payload string `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
ShortChannelId string `protobuf:"bytes,2,opt,name=short_channel_id,json=shortChannelId,proto3" json:"short_channel_id,omitempty"`
|
||||
ForwardMsat uint64 `protobuf:"varint,3,opt,name=forward_msat,json=forwardMsat,proto3" json:"forward_msat,omitempty"`
|
||||
OutgoingCltvValue uint32 `protobuf:"varint,4,opt,name=outgoing_cltv_value,json=outgoingCltvValue,proto3" json:"outgoing_cltv_value,omitempty"`
|
||||
SharedSecret string `protobuf:"bytes,5,opt,name=shared_secret,json=sharedSecret,proto3" json:"shared_secret,omitempty"`
|
||||
NextOnion string `protobuf:"bytes,6,opt,name=next_onion,json=nextOnion,proto3" json:"next_onion,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Onion) Reset() {
|
||||
*x = Onion{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Onion) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Onion) ProtoMessage() {}
|
||||
|
||||
func (x *Onion) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Onion.ProtoReflect.Descriptor instead.
|
||||
func (*Onion) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Onion) GetPayload() string {
|
||||
if x != nil {
|
||||
return x.Payload
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Onion) GetShortChannelId() string {
|
||||
if x != nil {
|
||||
return x.ShortChannelId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Onion) GetForwardMsat() uint64 {
|
||||
if x != nil {
|
||||
return x.ForwardMsat
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Onion) GetOutgoingCltvValue() uint32 {
|
||||
if x != nil {
|
||||
return x.OutgoingCltvValue
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Onion) GetSharedSecret() string {
|
||||
if x != nil {
|
||||
return x.SharedSecret
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Onion) GetNextOnion() string {
|
||||
if x != nil {
|
||||
return x.NextOnion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Htlc struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ShortChannelId string `protobuf:"bytes,1,opt,name=short_channel_id,json=shortChannelId,proto3" json:"short_channel_id,omitempty"`
|
||||
Id uint64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"`
|
||||
AmountMsat uint64 `protobuf:"varint,3,opt,name=amount_msat,json=amountMsat,proto3" json:"amount_msat,omitempty"`
|
||||
CltvExpiry uint32 `protobuf:"varint,4,opt,name=cltv_expiry,json=cltvExpiry,proto3" json:"cltv_expiry,omitempty"`
|
||||
CltvExpiryRelative uint32 `protobuf:"varint,5,opt,name=cltv_expiry_relative,json=cltvExpiryRelative,proto3" json:"cltv_expiry_relative,omitempty"`
|
||||
PaymentHash string `protobuf:"bytes,6,opt,name=payment_hash,json=paymentHash,proto3" json:"payment_hash,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Htlc) Reset() {
|
||||
*x = Htlc{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Htlc) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Htlc) ProtoMessage() {}
|
||||
|
||||
func (x *Htlc) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Htlc.ProtoReflect.Descriptor instead.
|
||||
func (*Htlc) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *Htlc) GetShortChannelId() string {
|
||||
if x != nil {
|
||||
return x.ShortChannelId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Htlc) GetId() uint64 {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Htlc) GetAmountMsat() uint64 {
|
||||
if x != nil {
|
||||
return x.AmountMsat
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Htlc) GetCltvExpiry() uint32 {
|
||||
if x != nil {
|
||||
return x.CltvExpiry
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Htlc) GetCltvExpiryRelative() uint32 {
|
||||
if x != nil {
|
||||
return x.CltvExpiryRelative
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *Htlc) GetPaymentHash() string {
|
||||
if x != nil {
|
||||
return x.PaymentHash
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type HtlcResolution struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Correlationid string `protobuf:"bytes,1,opt,name=correlationid,proto3" json:"correlationid,omitempty"`
|
||||
// Types that are assignable to Outcome:
|
||||
// *HtlcResolution_Fail
|
||||
// *HtlcResolution_Continue
|
||||
// *HtlcResolution_Resolve
|
||||
Outcome isHtlcResolution_Outcome `protobuf_oneof:"outcome"`
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) Reset() {
|
||||
*x = HtlcResolution{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcResolution) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcResolution) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcResolution.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcResolution) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) GetCorrelationid() string {
|
||||
if x != nil {
|
||||
return x.Correlationid
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *HtlcResolution) GetOutcome() isHtlcResolution_Outcome {
|
||||
if m != nil {
|
||||
return m.Outcome
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) GetFail() *HtlcFail {
|
||||
if x, ok := x.GetOutcome().(*HtlcResolution_Fail); ok {
|
||||
return x.Fail
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) GetContinue() *HtlcContinue {
|
||||
if x, ok := x.GetOutcome().(*HtlcResolution_Continue); ok {
|
||||
return x.Continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcResolution) GetResolve() *HtlcResolve {
|
||||
if x, ok := x.GetOutcome().(*HtlcResolution_Resolve); ok {
|
||||
return x.Resolve
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isHtlcResolution_Outcome interface {
|
||||
isHtlcResolution_Outcome()
|
||||
}
|
||||
|
||||
type HtlcResolution_Fail struct {
|
||||
Fail *HtlcFail `protobuf:"bytes,2,opt,name=fail,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HtlcResolution_Continue struct {
|
||||
Continue *HtlcContinue `protobuf:"bytes,3,opt,name=continue,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HtlcResolution_Resolve struct {
|
||||
Resolve *HtlcResolve `protobuf:"bytes,4,opt,name=resolve,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*HtlcResolution_Fail) isHtlcResolution_Outcome() {}
|
||||
|
||||
func (*HtlcResolution_Continue) isHtlcResolution_Outcome() {}
|
||||
|
||||
func (*HtlcResolution_Resolve) isHtlcResolution_Outcome() {}
|
||||
|
||||
type HtlcContinue struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Payload *string `protobuf:"bytes,1,opt,name=payload,proto3,oneof" json:"payload,omitempty"`
|
||||
ForwardTo *string `protobuf:"bytes,2,opt,name=forward_to,json=forwardTo,proto3,oneof" json:"forward_to,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HtlcContinue) Reset() {
|
||||
*x = HtlcContinue{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcContinue) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcContinue) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcContinue) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcContinue.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcContinue) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *HtlcContinue) GetPayload() string {
|
||||
if x != nil && x.Payload != nil {
|
||||
return *x.Payload
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HtlcContinue) GetForwardTo() string {
|
||||
if x != nil && x.ForwardTo != nil {
|
||||
return *x.ForwardTo
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type HtlcFail struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to Failure:
|
||||
// *HtlcFail_FailureMessage
|
||||
// *HtlcFail_FailureOnion
|
||||
Failure isHtlcFail_Failure `protobuf_oneof:"failure"`
|
||||
}
|
||||
|
||||
func (x *HtlcFail) Reset() {
|
||||
*x = HtlcFail{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcFail) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcFail) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcFail) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcFail.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcFail) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (m *HtlcFail) GetFailure() isHtlcFail_Failure {
|
||||
if m != nil {
|
||||
return m.Failure
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *HtlcFail) GetFailureMessage() string {
|
||||
if x, ok := x.GetFailure().(*HtlcFail_FailureMessage); ok {
|
||||
return x.FailureMessage
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *HtlcFail) GetFailureOnion() string {
|
||||
if x, ok := x.GetFailure().(*HtlcFail_FailureOnion); ok {
|
||||
return x.FailureOnion
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type isHtlcFail_Failure interface {
|
||||
isHtlcFail_Failure()
|
||||
}
|
||||
|
||||
type HtlcFail_FailureMessage struct {
|
||||
FailureMessage string `protobuf:"bytes,1,opt,name=failure_message,json=failureMessage,proto3,oneof"`
|
||||
}
|
||||
|
||||
type HtlcFail_FailureOnion struct {
|
||||
FailureOnion string `protobuf:"bytes,2,opt,name=failure_onion,json=failureOnion,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*HtlcFail_FailureMessage) isHtlcFail_Failure() {}
|
||||
|
||||
func (*HtlcFail_FailureOnion) isHtlcFail_Failure() {}
|
||||
|
||||
type HtlcResolve struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PaymentKey string `protobuf:"bytes,1,opt,name=payment_key,json=paymentKey,proto3" json:"payment_key,omitempty"`
|
||||
}
|
||||
|
||||
func (x *HtlcResolve) Reset() {
|
||||
*x = HtlcResolve{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_cln_plugin_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *HtlcResolve) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*HtlcResolve) ProtoMessage() {}
|
||||
|
||||
func (x *HtlcResolve) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_cln_plugin_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use HtlcResolve.ProtoReflect.Descriptor instead.
|
||||
func (*HtlcResolve) Descriptor() ([]byte, []int) {
|
||||
return file_cln_plugin_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *HtlcResolve) GetPaymentKey() string {
|
||||
if x != nil {
|
||||
return x.PaymentKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_cln_plugin_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_cln_plugin_proto_rawDesc = []byte{
|
||||
0x0a, 0x10, 0x63, 0x6c, 0x6e, 0x5f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x22, 0x8c, 0x01, 0x0a, 0x0c, 0x48, 0x74, 0x6c, 0x63, 0x41, 0x63, 0x63, 0x65, 0x70,
|
||||
0x74, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72,
|
||||
0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x05, 0x6f, 0x6e, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x06, 0x2e, 0x4f, 0x6e, 0x69, 0x6f, 0x6e,
|
||||
0x52, 0x05, 0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x04, 0x68, 0x74, 0x6c, 0x63, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x04, 0x68, 0x74,
|
||||
0x6c, 0x63, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x74, 0x6f,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x54,
|
||||
0x6f, 0x22, 0xe2, 0x01, 0x0a, 0x05, 0x4f, 0x6e, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x70,
|
||||
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61,
|
||||
0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x63,
|
||||
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12,
|
||||
0x21, 0x0a, 0x0c, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x4d, 0x73,
|
||||
0x61, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x5f, 0x63,
|
||||
0x6c, 0x74, 0x76, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||
0x11, 0x6f, 0x75, 0x74, 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x43, 0x6c, 0x74, 0x76, 0x56, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x63,
|
||||
0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x65,
|
||||
0x64, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x5f,
|
||||
0x6f, 0x6e, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x65, 0x78,
|
||||
0x74, 0x4f, 0x6e, 0x69, 0x6f, 0x6e, 0x22, 0xd7, 0x01, 0x0a, 0x04, 0x48, 0x74, 0x6c, 0x63, 0x12,
|
||||
0x28, 0x0a, 0x10, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x68, 0x6f, 0x72, 0x74,
|
||||
0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6d, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x5f, 0x6d, 0x73, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a,
|
||||
0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x73, 0x61, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x6c,
|
||||
0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||
0x0a, 0x63, 0x6c, 0x74, 0x76, 0x45, 0x78, 0x70, 0x69, 0x72, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x63,
|
||||
0x6c, 0x74, 0x76, 0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74,
|
||||
0x69, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x63, 0x6c, 0x74, 0x76, 0x45,
|
||||
0x78, 0x70, 0x69, 0x72, 0x79, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x12, 0x21, 0x0a,
|
||||
0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x06, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x61, 0x73, 0x68,
|
||||
0x22, 0xb9, 0x01, 0x0a, 0x0e, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x72, 0x72,
|
||||
0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x04, 0x66, 0x61, 0x69,
|
||||
0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x46, 0x61,
|
||||
0x69, 0x6c, 0x48, 0x00, 0x52, 0x04, 0x66, 0x61, 0x69, 0x6c, 0x12, 0x2b, 0x0a, 0x08, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x48,
|
||||
0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63,
|
||||
0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x12, 0x28, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x6f, 0x6c,
|
||||
0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x52,
|
||||
0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x48, 0x00, 0x52, 0x07, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76,
|
||||
0x65, 0x42, 0x09, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x63, 0x6f, 0x6d, 0x65, 0x22, 0x6c, 0x0a, 0x0c,
|
||||
0x48, 0x74, 0x6c, 0x63, 0x43, 0x6f, 0x6e, 0x74, 0x69, 0x6e, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x07,
|
||||
0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
|
||||
0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x66,
|
||||
0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48,
|
||||
0x01, 0x52, 0x09, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x54, 0x6f, 0x88, 0x01, 0x01, 0x42,
|
||||
0x0a, 0x0a, 0x08, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f,
|
||||
0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x5f, 0x74, 0x6f, 0x22, 0x67, 0x0a, 0x08, 0x48, 0x74,
|
||||
0x6c, 0x63, 0x46, 0x61, 0x69, 0x6c, 0x12, 0x29, 0x0a, 0x0f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72,
|
||||
0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48,
|
||||
0x00, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x12, 0x25, 0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x6f, 0x6e, 0x69,
|
||||
0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x66, 0x61, 0x69, 0x6c,
|
||||
0x75, 0x72, 0x65, 0x4f, 0x6e, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x66, 0x61, 0x69, 0x6c,
|
||||
0x75, 0x72, 0x65, 0x22, 0x2e, 0x0a, 0x0b, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c,
|
||||
0x76, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65,
|
||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74,
|
||||
0x4b, 0x65, 0x79, 0x32, 0x3d, 0x0a, 0x09, 0x43, 0x6c, 0x6e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e,
|
||||
0x12, 0x30, 0x0a, 0x0a, 0x48, 0x74, 0x6c, 0x63, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x0f,
|
||||
0x2e, 0x48, 0x74, 0x6c, 0x63, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x1a,
|
||||
0x0d, 0x2e, 0x48, 0x74, 0x6c, 0x63, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x28, 0x01,
|
||||
0x30, 0x01, 0x42, 0x28, 0x5a, 0x26, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||
0x2f, 0x62, 0x72, 0x65, 0x65, 0x7a, 0x2f, 0x6c, 0x73, 0x70, 0x64, 0x2f, 0x63, 0x6c, 0x6e, 0x5f,
|
||||
0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_cln_plugin_proto_rawDescOnce sync.Once
|
||||
file_cln_plugin_proto_rawDescData = file_cln_plugin_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_cln_plugin_proto_rawDescGZIP() []byte {
|
||||
file_cln_plugin_proto_rawDescOnce.Do(func() {
|
||||
file_cln_plugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_cln_plugin_proto_rawDescData)
|
||||
})
|
||||
return file_cln_plugin_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_cln_plugin_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_cln_plugin_proto_goTypes = []interface{}{
|
||||
(*HtlcAccepted)(nil), // 0: HtlcAccepted
|
||||
(*Onion)(nil), // 1: Onion
|
||||
(*Htlc)(nil), // 2: Htlc
|
||||
(*HtlcResolution)(nil), // 3: HtlcResolution
|
||||
(*HtlcContinue)(nil), // 4: HtlcContinue
|
||||
(*HtlcFail)(nil), // 5: HtlcFail
|
||||
(*HtlcResolve)(nil), // 6: HtlcResolve
|
||||
}
|
||||
var file_cln_plugin_proto_depIdxs = []int32{
|
||||
1, // 0: HtlcAccepted.onion:type_name -> Onion
|
||||
2, // 1: HtlcAccepted.htlc:type_name -> Htlc
|
||||
5, // 2: HtlcResolution.fail:type_name -> HtlcFail
|
||||
4, // 3: HtlcResolution.continue:type_name -> HtlcContinue
|
||||
6, // 4: HtlcResolution.resolve:type_name -> HtlcResolve
|
||||
3, // 5: ClnPlugin.HtlcStream:input_type -> HtlcResolution
|
||||
0, // 6: ClnPlugin.HtlcStream:output_type -> HtlcAccepted
|
||||
6, // [6:7] is the sub-list for method output_type
|
||||
5, // [5:6] is the sub-list for method input_type
|
||||
5, // [5:5] is the sub-list for extension type_name
|
||||
5, // [5:5] is the sub-list for extension extendee
|
||||
0, // [0:5] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_cln_plugin_proto_init() }
|
||||
func file_cln_plugin_proto_init() {
|
||||
if File_cln_plugin_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_cln_plugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcAccepted); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Onion); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Htlc); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcResolution); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcContinue); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcFail); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*HtlcResolve); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[3].OneofWrappers = []interface{}{
|
||||
(*HtlcResolution_Fail)(nil),
|
||||
(*HtlcResolution_Continue)(nil),
|
||||
(*HtlcResolution_Resolve)(nil),
|
||||
}
|
||||
file_cln_plugin_proto_msgTypes[4].OneofWrappers = []interface{}{}
|
||||
file_cln_plugin_proto_msgTypes[5].OneofWrappers = []interface{}{
|
||||
(*HtlcFail_FailureMessage)(nil),
|
||||
(*HtlcFail_FailureOnion)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_cln_plugin_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_cln_plugin_proto_goTypes,
|
||||
DependencyIndexes: file_cln_plugin_proto_depIdxs,
|
||||
MessageInfos: file_cln_plugin_proto_msgTypes,
|
||||
}.Build()
|
||||
File_cln_plugin_proto = out.File
|
||||
file_cln_plugin_proto_rawDesc = nil
|
||||
file_cln_plugin_proto_goTypes = nil
|
||||
file_cln_plugin_proto_depIdxs = nil
|
||||
}
|
||||
@@ -1,46 +1,56 @@
|
||||
syntax = "proto3";
|
||||
option go_package="github.com/breez/lspd/cln_plugin";
|
||||
option go_package="github.com/breez/lspd/cln_plugin/proto";
|
||||
|
||||
service ClnPlugin {
|
||||
rpc HtlcStream(stream HtlcResolution) returns (stream HtlcAccepted);
|
||||
}
|
||||
|
||||
message HtlcAccepted {
|
||||
uint64 correlationid = 1;
|
||||
string correlationid = 1;
|
||||
Onion onion = 2;
|
||||
HtlcOffer htlc = 3;
|
||||
Htlc htlc = 3;
|
||||
string forward_to = 4;
|
||||
}
|
||||
|
||||
message Onion {
|
||||
bytes payload = 1;
|
||||
uint64 short_channel_id = 2;
|
||||
uint64 forward_amount_msat = 3;
|
||||
string payload = 1;
|
||||
string short_channel_id = 2;
|
||||
uint64 forward_msat = 3;
|
||||
uint32 outgoing_cltv_value = 4;
|
||||
string shared_secret = 5;
|
||||
string next_onion = 6;
|
||||
}
|
||||
|
||||
message HtlcOffer {
|
||||
uint64 amount_msat = 2;
|
||||
uint32 cltv_expiry_relative = 3;
|
||||
message Htlc {
|
||||
string short_channel_id = 1;
|
||||
uint64 id = 2;
|
||||
uint64 amount_msat = 3;
|
||||
uint32 cltv_expiry = 4;
|
||||
bytes payment_hash = 5;
|
||||
uint32 cltv_expiry_relative = 5;
|
||||
string payment_hash = 6;
|
||||
}
|
||||
|
||||
message HtlcResolution {
|
||||
uint64 correlationid = 1;
|
||||
string correlationid = 1;
|
||||
oneof outcome {
|
||||
HtlcFail fail = 2;
|
||||
HtlcContinue continue = 3;
|
||||
HtlcContinueWith continue_with = 4;
|
||||
HtlcResolve resolve = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message HtlcFail {
|
||||
bytes failure_message = 1;
|
||||
}
|
||||
|
||||
message HtlcContinue {
|
||||
optional string payload = 1;
|
||||
optional string forward_to = 2;
|
||||
}
|
||||
|
||||
message HtlcContinueWith {
|
||||
bytes channel_id = 1;
|
||||
bytes payload = 2;
|
||||
message HtlcFail {
|
||||
oneof failure {
|
||||
string failure_message = 1;
|
||||
string failure_onion = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message HtlcResolve {
|
||||
string payment_key = 1;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.2.0
|
||||
// - protoc v3.21.8
|
||||
// - protoc v3.21.12
|
||||
// source: cln_plugin.proto
|
||||
|
||||
package cln_plugin
|
||||
package proto
|
||||
|
||||
import (
|
||||
context "context"
|
||||
@@ -1 +0,0 @@
|
||||
LISTEN_ADDRESS=<address the lsp cln plugin listens on (ip:port)><address the lsp cln plugin listens on (ip:port)>
|
||||
@@ -8,64 +8,103 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/breez/lspd/cln_plugin/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
var receiveWaitDelay = time.Millisecond * 200
|
||||
|
||||
// A subscription represents a grpc client that is connected to the server.
|
||||
type subscription struct {
|
||||
stream ClnPlugin_HtlcStreamServer
|
||||
stream proto.ClnPlugin_HtlcStreamServer
|
||||
done chan struct{}
|
||||
err chan error
|
||||
}
|
||||
|
||||
// Internal htlc_accepted message meant for the sendQueue.
|
||||
type htlcAcceptedMsg struct {
|
||||
id string
|
||||
htlc *HtlcAccepted
|
||||
timeout <-chan time.Time
|
||||
}
|
||||
|
||||
// Internal htlc result message meant for the recvQueue.
|
||||
type htlcResultMsg struct {
|
||||
id string
|
||||
result interface{}
|
||||
}
|
||||
|
||||
type server struct {
|
||||
ClnPluginServer
|
||||
proto.ClnPluginServer
|
||||
listenAddress string
|
||||
subscriberTimeout time.Duration
|
||||
grpcServer *grpc.Server
|
||||
startMtx sync.Mutex
|
||||
corrMtx sync.Mutex
|
||||
mtx sync.Mutex
|
||||
subscription *subscription
|
||||
newSubscriber chan struct{}
|
||||
started chan struct{}
|
||||
startError chan error
|
||||
done chan struct{}
|
||||
correlations map[uint64]chan *HtlcResolution
|
||||
index uint64
|
||||
sendQueue chan *htlcAcceptedMsg
|
||||
recvQueue chan *htlcResultMsg
|
||||
}
|
||||
|
||||
func NewServer(listenAddress string) *server {
|
||||
// Creates a new grpc server
|
||||
func NewServer(listenAddress string, subscriberTimeout time.Duration) *server {
|
||||
// TODO: Set a sane max queue size
|
||||
return &server{
|
||||
listenAddress: listenAddress,
|
||||
newSubscriber: make(chan struct{}, 1),
|
||||
correlations: make(map[uint64]chan *HtlcResolution),
|
||||
index: 0,
|
||||
subscriberTimeout: subscriberTimeout,
|
||||
sendQueue: make(chan *htlcAcceptedMsg, 10000),
|
||||
recvQueue: make(chan *htlcResultMsg, 10000),
|
||||
started: make(chan struct{}),
|
||||
startError: make(chan error, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the grpc server. Blocks until the servver is stopped. WaitStarted can
|
||||
// be called to ensure the server is started without errors if this function
|
||||
// is run as a goroutine.
|
||||
func (s *server) Start() error {
|
||||
s.startMtx.Lock()
|
||||
s.mtx.Lock()
|
||||
if s.grpcServer != nil {
|
||||
s.startMtx.Unlock()
|
||||
s.mtx.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
lis, err := net.Listen("tcp", s.listenAddress)
|
||||
if err != nil {
|
||||
log.Printf("ERROR Server failed to listen: %v", err)
|
||||
s.startMtx.Unlock()
|
||||
s.startError <- err
|
||||
s.mtx.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
s.done = make(chan struct{})
|
||||
s.newSubscriber = make(chan struct{})
|
||||
s.grpcServer = grpc.NewServer()
|
||||
s.startMtx.Unlock()
|
||||
RegisterClnPluginServer(s.grpcServer, s)
|
||||
s.mtx.Unlock()
|
||||
proto.RegisterClnPluginServer(s.grpcServer, s)
|
||||
|
||||
log.Printf("Server starting to listen on %s.", s.listenAddress)
|
||||
go s.listenHtlcRequests()
|
||||
go s.listenHtlcResponses()
|
||||
close(s.started)
|
||||
return s.grpcServer.Serve(lis)
|
||||
}
|
||||
|
||||
// Waits until the server has started, or errored during startup.
|
||||
func (s *server) WaitStarted() error {
|
||||
select {
|
||||
case <-s.started:
|
||||
return nil
|
||||
case err := <-s.startError:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Stops all work from the grpc server immediately.
|
||||
func (s *server) Stop() {
|
||||
s.startMtx.Lock()
|
||||
defer s.startMtx.Unlock()
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
log.Printf("Server Stop() called.")
|
||||
if s.grpcServer == nil {
|
||||
return
|
||||
@@ -76,36 +115,38 @@ func (s *server) Stop() {
|
||||
s.grpcServer = nil
|
||||
}
|
||||
|
||||
func (s *server) HtlcStream(stream ClnPlugin_HtlcStreamServer) error {
|
||||
// Grpc method that is called when a new client subscribes. There can only be
|
||||
// one subscriber active at a time. If there is an error receiving or sending
|
||||
// from or to the subscriber, the subscription is closed.
|
||||
func (s *server) HtlcStream(stream proto.ClnPlugin_HtlcStreamServer) error {
|
||||
log.Printf("Got HTLC stream subscription request.")
|
||||
s.startMtx.Lock()
|
||||
s.mtx.Lock()
|
||||
if s.subscription != nil {
|
||||
s.startMtx.Unlock()
|
||||
s.mtx.Unlock()
|
||||
return fmt.Errorf("already subscribed")
|
||||
}
|
||||
|
||||
sb := &subscription{
|
||||
stream: stream,
|
||||
done: make(chan struct{}),
|
||||
err: make(chan error, 1),
|
||||
}
|
||||
s.subscription = sb
|
||||
s.newSubscriber <- struct{}{}
|
||||
s.startMtx.Unlock()
|
||||
|
||||
// Notify listeners that a new subscriber is active. Replace the chan with
|
||||
// a new one immediately in case this subscriber is dropped later.
|
||||
close(s.newSubscriber)
|
||||
s.newSubscriber = make(chan struct{})
|
||||
s.mtx.Unlock()
|
||||
|
||||
defer func() {
|
||||
s.startMtx.Lock()
|
||||
s.subscription = nil
|
||||
close(sb.done)
|
||||
s.startMtx.Unlock()
|
||||
s.removeSubscriptionIfUnchanged(sb, nil)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
<-stream.Context().Done()
|
||||
log.Printf("HtlcStream context is done. Removing subscriber: %v", stream.Context().Err())
|
||||
s.startMtx.Lock()
|
||||
s.subscription = nil
|
||||
close(sb.done)
|
||||
s.startMtx.Unlock()
|
||||
s.removeSubscriptionIfUnchanged(sb, stream.Context().Err())
|
||||
}()
|
||||
|
||||
select {
|
||||
@@ -115,82 +156,179 @@ func (s *server) HtlcStream(stream ClnPlugin_HtlcStreamServer) error {
|
||||
case <-sb.done:
|
||||
log.Printf("HTLC stream signalled done. Return EOF.")
|
||||
return io.EOF
|
||||
case err := <-sb.err:
|
||||
log.Printf("HTLC stream signalled error. Return %v", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) Send(h *HtlcAccepted) *HtlcResolution {
|
||||
sb := s.subscription
|
||||
if sb == nil {
|
||||
log.Printf("No subscribers available. Ignoring HtlcAccepted %+v", h)
|
||||
return s.defaultResolution()
|
||||
}
|
||||
|
||||
c := make(chan *HtlcResolution)
|
||||
s.corrMtx.Lock()
|
||||
s.index++
|
||||
index := s.index
|
||||
s.correlations[index] = c
|
||||
s.corrMtx.Unlock()
|
||||
|
||||
h.Correlationid = index
|
||||
|
||||
defer func() {
|
||||
s.corrMtx.Lock()
|
||||
delete(s.correlations, index)
|
||||
s.corrMtx.Unlock()
|
||||
close(c)
|
||||
}()
|
||||
|
||||
log.Printf("Sending HtlcAccepted: %+v", h)
|
||||
err := sb.stream.Send(h)
|
||||
if err != nil {
|
||||
// TODO: Close the connection? Reset the subscriber?
|
||||
log.Printf("Send() errored, Correlationid: %d: %v", index, err)
|
||||
return s.defaultResolution()
|
||||
// Enqueues a htlc_accepted message for send to the grpc client.
|
||||
func (s *server) Send(id string, h *HtlcAccepted) {
|
||||
s.sendQueue <- &htlcAcceptedMsg{
|
||||
id: id,
|
||||
htlc: h,
|
||||
timeout: time.After(s.subscriberTimeout),
|
||||
}
|
||||
}
|
||||
|
||||
// Receives the next htlc resolution message from the grpc client. Returns id
|
||||
// and message. Blocks until a message is available. Returns a nil message if
|
||||
// the server is done.
|
||||
func (s *server) Receive() (string, interface{}) {
|
||||
select {
|
||||
case <-s.done:
|
||||
log.Printf("Signalled done while waiting for htlc resolution, Correlationid: %d, Ignoring: %+v", index, h)
|
||||
return s.defaultResolution()
|
||||
case resolution := <-c:
|
||||
log.Printf("Got resolution, Correlationid: %d: %+v", index, h)
|
||||
return resolution
|
||||
return "", nil
|
||||
case msg := <-s.recvQueue:
|
||||
return msg.id, msg.result
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) recv() *HtlcResolution {
|
||||
// Helper function that blocks until a message from a grpc client is received
|
||||
// or the server stops. Either returns a received message, or nil if the server
|
||||
// has stopped.
|
||||
func (s *server) recv() *proto.HtlcResolution {
|
||||
for {
|
||||
// make a copy of the used fields, to make sure state updates don't
|
||||
// surprise us. The newSubscriber chan is swapped whenever a new
|
||||
// subscriber arrives.
|
||||
s.mtx.Lock()
|
||||
sb := s.subscription
|
||||
ns := s.newSubscriber
|
||||
s.mtx.Unlock()
|
||||
|
||||
if sb == nil {
|
||||
log.Printf("Got no subscribers for receive. Waiting for subscriber.")
|
||||
select {
|
||||
case <-s.done:
|
||||
log.Printf("Done signalled, stopping receive.")
|
||||
return s.defaultResolution()
|
||||
case <-s.newSubscriber:
|
||||
return nil
|
||||
case <-ns:
|
||||
log.Printf("New subscription available for receive, continue receive.")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// There is a subscription active. Attempt to receive a message.
|
||||
r, err := sb.stream.Recv()
|
||||
if err == nil {
|
||||
log.Printf("Received HtlcResolution %+v", r)
|
||||
return r
|
||||
}
|
||||
|
||||
// TODO: close the subscription??
|
||||
log.Printf("Recv() errored, waiting %v: %v", receiveWaitDelay, err)
|
||||
// Receiving the message failed, so the subscription is broken. Remove
|
||||
// it if it hasn't been updated already. We'll try receiving again in
|
||||
// the next iteration of the for loop.
|
||||
log.Printf("Recv() errored, removing subscription: %v", err)
|
||||
s.removeSubscriptionIfUnchanged(sb, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Stops and removes the subscription if this is the currently active
|
||||
// subscription. If the subscription was changed in the meantime, this function
|
||||
// does nothing.
|
||||
func (s *server) removeSubscriptionIfUnchanged(sb *subscription, err error) {
|
||||
s.mtx.Lock()
|
||||
// If the subscription reference hasn't changed yet in the meantime, kill it.
|
||||
if s.subscription == sb {
|
||||
if err == nil {
|
||||
close(sb.done)
|
||||
} else {
|
||||
sb.err <- err
|
||||
}
|
||||
s.subscription = nil
|
||||
}
|
||||
s.mtx.Unlock()
|
||||
}
|
||||
|
||||
// Listens to sendQueue for htlc_accepted requests from cln. The message will be
|
||||
// held until a subscriber is active, or the subscriber timeout expires. The
|
||||
// messages are sent to the grpc client in fifo order.
|
||||
func (s *server) listenHtlcRequests() {
|
||||
for {
|
||||
select {
|
||||
case <-s.done:
|
||||
log.Printf("Done signalled, stopping receive.")
|
||||
return s.defaultResolution()
|
||||
case <-time.After(receiveWaitDelay):
|
||||
log.Printf("listenHtlcRequests received done. Stop listening.")
|
||||
return
|
||||
case msg := <-s.sendQueue:
|
||||
s.handleHtlcAccepted(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to send a htlc_accepted message to the grpc client. The message will
|
||||
// be held until a subscriber is active, or the subscriber timeout expires.
|
||||
func (s *server) handleHtlcAccepted(msg *htlcAcceptedMsg) {
|
||||
for {
|
||||
s.mtx.Lock()
|
||||
sb := s.subscription
|
||||
ns := s.newSubscriber
|
||||
s.mtx.Unlock()
|
||||
|
||||
// If there is no active subscription, wait until there is a new
|
||||
// subscriber, or the message times out.
|
||||
if sb == nil {
|
||||
select {
|
||||
case <-s.done:
|
||||
log.Printf("handleHtlcAccepted received server done. Stop processing.")
|
||||
return
|
||||
case <-ns:
|
||||
log.Printf("got a new subscriber. continue handleHtlcAccepted.")
|
||||
continue
|
||||
case <-msg.timeout:
|
||||
log.Printf(
|
||||
"WARNING: htlc with id '%s' timed out after '%v' waiting "+
|
||||
"for grpc subscriber: %+v",
|
||||
msg.id,
|
||||
s.subscriberTimeout,
|
||||
msg.htlc,
|
||||
)
|
||||
s.recvQueue <- &htlcResultMsg{
|
||||
id: msg.id,
|
||||
result: s.defaultResult(),
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// There is a subscriber. Attempt to send the htlc_accepted message.
|
||||
err := sb.stream.Send(&proto.HtlcAccepted{
|
||||
Correlationid: msg.id,
|
||||
Onion: &proto.Onion{
|
||||
Payload: msg.htlc.Onion.Payload,
|
||||
ShortChannelId: msg.htlc.Onion.ShortChannelId,
|
||||
ForwardMsat: msg.htlc.Onion.ForwardMsat,
|
||||
OutgoingCltvValue: msg.htlc.Onion.OutgoingCltvValue,
|
||||
SharedSecret: msg.htlc.Onion.SharedSecret,
|
||||
NextOnion: msg.htlc.Onion.NextOnion,
|
||||
},
|
||||
Htlc: &proto.Htlc{
|
||||
ShortChannelId: msg.htlc.Htlc.ShortChannelId,
|
||||
Id: msg.htlc.Htlc.Id,
|
||||
AmountMsat: msg.htlc.Htlc.AmountMsat,
|
||||
CltvExpiry: msg.htlc.Htlc.CltvExpiry,
|
||||
CltvExpiryRelative: msg.htlc.Htlc.CltvExpiryRelative,
|
||||
PaymentHash: msg.htlc.Htlc.PaymentHash,
|
||||
},
|
||||
ForwardTo: msg.htlc.ForwardTo,
|
||||
})
|
||||
|
||||
// If there is no error, we're done.
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// If we end up here, there was an error sending the message to the
|
||||
// grpc client.
|
||||
log.Printf("Error sending htlc_accepted message to subscriber. "+
|
||||
"Removing subscription: %v", err)
|
||||
s.removeSubscriptionIfUnchanged(sb, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Listens to htlc responses from the grpc client and appends them to the
|
||||
// receive queue. The messages from the receive queue are read in the Receive
|
||||
// function.
|
||||
func (s *server) listenHtlcResponses() {
|
||||
for {
|
||||
select {
|
||||
@@ -198,23 +336,77 @@ func (s *server) listenHtlcResponses() {
|
||||
log.Printf("listenHtlcResponses received done. Stopping listening.")
|
||||
return
|
||||
default:
|
||||
response := s.recv()
|
||||
s.corrMtx.Lock()
|
||||
correlation, ok := s.correlations[response.Correlationid]
|
||||
s.corrMtx.Unlock()
|
||||
if ok {
|
||||
correlation <- response
|
||||
} else {
|
||||
log.Printf("Got HTLC resolution that could not be correlated: %+v", response)
|
||||
resp := s.recv()
|
||||
s.recvQueue <- &htlcResultMsg{
|
||||
id: resp.Correlationid,
|
||||
result: s.mapResult(resp.Outcome),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) defaultResolution() *HtlcResolution {
|
||||
return &HtlcResolution{
|
||||
Outcome: &HtlcResolution_Continue{
|
||||
Continue: &HtlcContinue{},
|
||||
},
|
||||
// Maps a grpc result to the corresponding result for cln. The cln message
|
||||
// is a raw json message, so it's easiest to use a map directly.
|
||||
func (s *server) mapResult(outcome interface{}) interface{} {
|
||||
// result: continue
|
||||
cont, ok := outcome.(*proto.HtlcResolution_Continue)
|
||||
if ok {
|
||||
result := map[string]interface{}{
|
||||
"result": "continue",
|
||||
}
|
||||
|
||||
if cont.Continue.ForwardTo != nil {
|
||||
result["forward_to"] = *cont.Continue.ForwardTo
|
||||
}
|
||||
|
||||
if cont.Continue.Payload != nil {
|
||||
result["payload"] = *cont.Continue.Payload
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// result: fail
|
||||
fail, ok := outcome.(*proto.HtlcResolution_Fail)
|
||||
if ok {
|
||||
result := map[string]interface{}{
|
||||
"result": "fail",
|
||||
}
|
||||
|
||||
fm, ok := fail.Fail.Failure.(*proto.HtlcFail_FailureMessage)
|
||||
if ok {
|
||||
result["failure_message"] = fm.FailureMessage
|
||||
}
|
||||
|
||||
fo, ok := fail.Fail.Failure.(*proto.HtlcFail_FailureOnion)
|
||||
if ok {
|
||||
result["failure_onion"] = fo.FailureOnion
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// result: resolve
|
||||
resolve, ok := outcome.(*proto.HtlcResolution_Resolve)
|
||||
if ok {
|
||||
result := map[string]interface{}{
|
||||
"result": "resolve",
|
||||
"payment_key": resolve.Resolve.PaymentKey,
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// On an unknown result we haven't implemented all possible cases from the
|
||||
// grpc message. We don't understand what's going on, so we'll return
|
||||
// result: continue.
|
||||
log.Printf("Unexpected htlc resolution type %T: %+v", outcome, outcome)
|
||||
return s.defaultResult()
|
||||
}
|
||||
|
||||
// Returns a result: continue message.
|
||||
func (s *server) defaultResult() interface{} {
|
||||
return map[string]interface{}{
|
||||
"result": "continue",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ type ClnLspNode struct {
|
||||
isInitialized bool
|
||||
mtx sync.Mutex
|
||||
pluginBinary string
|
||||
pluginFile string
|
||||
pluginAddress string
|
||||
}
|
||||
|
||||
@@ -43,7 +42,6 @@ type clnLspNodeRuntime struct {
|
||||
|
||||
func NewClnLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode {
|
||||
scriptDir := h.GetDirectory("lspd")
|
||||
pluginFile := filepath.Join(scriptDir, "htlc.sh")
|
||||
pluginBinary := *clnPluginExec
|
||||
pluginPort, err := lntest.GetPort()
|
||||
if err != nil {
|
||||
@@ -52,7 +50,8 @@ func NewClnLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode
|
||||
pluginAddress := fmt.Sprintf("127.0.0.1:%d", pluginPort)
|
||||
|
||||
args := []string{
|
||||
fmt.Sprintf("--plugin=%s", pluginFile),
|
||||
fmt.Sprintf("--plugin=%s", pluginBinary),
|
||||
fmt.Sprintf("--lsp.listen=%s", pluginAddress),
|
||||
fmt.Sprintf("--fee-base=%d", lspBaseFeeMsat),
|
||||
fmt.Sprintf("--fee-per-satoshi=%d", lspFeeRatePpm),
|
||||
fmt.Sprintf("--cltv-delta=%d", lspCltvDelta),
|
||||
@@ -79,7 +78,6 @@ func NewClnLspdNode(h *lntest.TestHarness, m *lntest.Miner, name string) LspNode
|
||||
logFilePath: logFilePath,
|
||||
lspBase: lspbase,
|
||||
pluginBinary: pluginBinary,
|
||||
pluginFile: pluginFile,
|
||||
pluginAddress: pluginAddress,
|
||||
}
|
||||
|
||||
@@ -102,16 +100,6 @@ func (c *ClnLspNode) Start() {
|
||||
Name: fmt.Sprintf("%s: lsp base", c.lspBase.name),
|
||||
Fn: c.lspBase.Stop,
|
||||
})
|
||||
|
||||
pluginContent := fmt.Sprintf(`#!/bin/bash
|
||||
export LISTEN_ADDRESS=%s
|
||||
%s`, c.pluginAddress, c.pluginBinary)
|
||||
|
||||
err = os.WriteFile(c.pluginFile, []byte(pluginContent), 0755)
|
||||
if err != nil {
|
||||
lntest.PerformCleanup(cleanups)
|
||||
c.harness.T.Fatalf("failed create lsp cln plugin file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.lightningNode.Start()
|
||||
|
||||
Reference in New Issue
Block a user