diff --git a/cln_interceptor.go b/cln_interceptor.go index ab082fd..194075c 100644 --- a/cln_interceptor.go +++ b/cln_interceptor.go @@ -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 } } diff --git a/cln_plugin/cln_messages.go b/cln_plugin/cln_messages.go new file mode 100644 index 0000000..aed6a5e --- /dev/null +++ b/cln_plugin/cln_messages.go @@ -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"` +} diff --git a/cln_plugin/cln_plugin.go b/cln_plugin/cln_plugin.go index e104f75..987fba6 100644 --- a/cln_plugin/cln_plugin.go +++ b/cln_plugin/cln_plugin.go @@ -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 { - server *server - plugin *glightning.Plugin + done chan struct{} + server *server + 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) - return err - } - - return nil +// 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) Stop() { - c.plugin.Stop() - c.server.Stop() -} +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, + ) + } + } -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) + for _, line := range strings.Split(scanner.Text(), "\n") { + c.log("info", line) + } + } } - }() + + }(in) + log.SetFlags(log.Ltime | log.Lshortfile) + log.SetOutput(out) } -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 +// 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() } - 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 +} + +// 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) + } } - 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 +} + +// 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 } - resp := c.server.Send(&HtlcAccepted{ - Onion: &Onion{ - Payload: payload, - ShortChannelId: uint64(*scid), - ForwardAmountMsat: event.Onion.ForwardAmount, - }, - Htlc: &HtlcOffer{ - AmountMsat: event.Htlc.AmountMilliSatoshi, - CltvExpiryRelative: uint32(event.Htlc.CltvExpiryRelative), - CltvExpiry: uint32(event.Htlc.CltvExpiry), - PaymentHash: ph, + // 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) 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), + ) + } +} + +// 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", + }, }, }) - - _, 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 } diff --git a/cln_plugin/cln_plugin.pb.go b/cln_plugin/cln_plugin.pb.go deleted file mode 100644 index 181a0f0..0000000 --- a/cln_plugin/cln_plugin.pb.go +++ /dev/null @@ -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 -} diff --git a/cln_plugin/cmd/main.go b/cln_plugin/cmd/main.go index 5a84e46..0fc934d 100644 --- a/cln_plugin/cmd/main.go +++ b/cln_plugin/cmd/main.go @@ -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() } diff --git a/cln_plugin/genproto.sh b/cln_plugin/genproto.sh index 226b0fe..e598c4e 100755 --- a/cln_plugin/genproto.sh +++ b/cln_plugin/genproto.sh @@ -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/* \ No newline at end of file +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 \ No newline at end of file diff --git a/cln_plugin/proto/cln_plugin.pb.go b/cln_plugin/proto/cln_plugin.pb.go new file mode 100644 index 0000000..72675d6 --- /dev/null +++ b/cln_plugin/proto/cln_plugin.pb.go @@ -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 +} diff --git a/cln_plugin/proto/cln_plugin.proto b/cln_plugin/proto/cln_plugin.proto index f284d40..be35059 100644 --- a/cln_plugin/proto/cln_plugin.proto +++ b/cln_plugin/proto/cln_plugin.proto @@ -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; -} \ No newline at end of file +message HtlcFail { + oneof failure { + string failure_message = 1; + string failure_onion = 2; + } +} + +message HtlcResolve { + string payment_key = 1; +} diff --git a/cln_plugin/cln_plugin_grpc.pb.go b/cln_plugin/proto/cln_plugin_grpc.pb.go similarity index 98% rename from cln_plugin/cln_plugin_grpc.pb.go rename to cln_plugin/proto/cln_plugin_grpc.pb.go index 3d4c9f9..1f8ccd2 100644 --- a/cln_plugin/cln_plugin_grpc.pb.go +++ b/cln_plugin/proto/cln_plugin_grpc.pb.go @@ -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" diff --git a/cln_plugin/sample.env b/cln_plugin/sample.env deleted file mode 100644 index 1dc0012..0000000 --- a/cln_plugin/sample.env +++ /dev/null @@ -1 +0,0 @@ -LISTEN_ADDRESS=
\ No newline at end of file diff --git a/cln_plugin/server.go b/cln_plugin/server.go index 7bf91ea..680b83a 100644 --- a/cln_plugin/server.go +++ b/cln_plugin/server.go @@ -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{} -} -type server struct { - ClnPluginServer - listenAddress string - grpcServer *grpc.Server - startMtx sync.Mutex - corrMtx sync.Mutex - subscription *subscription - newSubscriber chan struct{} - done chan struct{} - correlations map[uint64]chan *HtlcResolution - index uint64 + err chan error } -func NewServer(listenAddress string) *server { +// 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 { + proto.ClnPluginServer + listenAddress string + subscriberTimeout time.Duration + grpcServer *grpc.Server + mtx sync.Mutex + subscription *subscription + newSubscriber chan struct{} + started chan struct{} + startError chan error + done chan struct{} + sendQueue chan *htlcAcceptedMsg + recvQueue chan *htlcResultMsg +} + +// 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, + listenAddress: listenAddress, + 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", } } diff --git a/itest/cln_lspd_node.go b/itest/cln_lspd_node.go index cdbfdc8..c8731d3 100644 --- a/itest/cln_lspd_node.go +++ b/itest/cln_lspd_node.go @@ -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()