Skip to content

Commit

Permalink
Add cln channel acceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
yaslama committed Jul 3, 2023
1 parent 34646d5 commit 70d6afc
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 9 deletions.
37 changes: 37 additions & 0 deletions cln_plugin/channel_acceptor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package cln_plugin

import (
"encoding/json"
"fmt"

sj "go.starlark.net/lib/json"
"go.starlark.net/starlark"
)

func channelAcceptor(acceptScript string, method string, openChannel json.RawMessage) (json.RawMessage, error) {
reject, _ := json.Marshal(struct {
Result string `json:"result"`
}{Result: "reject"})

sd := starlark.StringDict{
"method": starlark.String(method),
"openchannel": starlark.String(openChannel),
}
for _, k := range sj.Module.Members.Keys() {
sd[k] = sj.Module.Members[k]
}
value, err := starlark.Eval(
&starlark.Thread{},
"",
acceptScript,
sd,
)
if err != nil {
return reject, err
}
s, ok := value.(starlark.String)
if !ok {
return reject, fmt.Errorf("not a string")
}
return json.RawMessage(s.GoString()), nil
}
146 changes: 137 additions & 9 deletions cln_plugin/cln_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
const (
SubscriberTimeoutOption = "lsp-subscribertimeout"
ListenAddressOption = "lsp-listen"
channelAcceptScript = "lsp-channel-accept-script"
)

var (
Expand All @@ -43,11 +44,12 @@ var (
)

type ClnPlugin struct {
done chan struct{}
server *server
in *os.File
out *bufio.Writer
writeMtx sync.Mutex
done chan struct{}
server *server
in *os.File
out *bufio.Writer
writeMtx sync.Mutex
channelAcceptScript string
}

func NewClnPlugin(in, out *os.File) *ClnPlugin {
Expand Down Expand Up @@ -210,6 +212,20 @@ func (c *ClnPlugin) processRequest(request *Request) {
c.handleShutdown(request)
case "htlc_accepted":
c.handleHtlcAccepted(request)
case "openchannel":
// handle open channel in a goroutine, because order doesn't matter.
go c.handleOpenChannel(request)
case "openchannel2":
// handle open channel in a goroutine, because order doesn't matter.
go c.handleOpenChannel(request)
case "getchannelacceptscript":
c.sendToCln(&Response{
JsonRpc: SpecVersion,
Id: request.Id,
Result: c.channelAcceptScript,
})
case "setchannelacceptscript":
c.handleSetChannelAcceptScript(request)
default:
c.sendError(
request.Id,
Expand Down Expand Up @@ -239,14 +255,28 @@ func (c *ClnPlugin) handleGetManifest(request *Request) {
"if no subscriber is active. golang duration string.",
Default: &DefaultSubscriberTimeout,
},
{
Name: channelAcceptScript,
Type: "string",
Description: "starlark script for channel acceptor.",
},
},
RpcMethods: []*RpcMethod{},
Dynamic: true,
Hooks: []Hook{
RpcMethods: []*RpcMethod{
{
Name: "getchannelacceptscript",
Description: "Get the startlark channel acceptor script",
},
{
Name: "htlc_accepted",
Name: "setchannelacceptscript",
Description: "Set the startlark channel acceptor script",
},
},
Dynamic: true,
Hooks: []Hook{
{Name: "htlc_accepted"},
{Name: "openchannel"},
{Name: "openchannel2"},
},
NonNumericIds: true,
Subscriptions: []string{
"shutdown",
Expand All @@ -270,6 +300,31 @@ func (c *ClnPlugin) handleInit(request *Request) {
return
}

// Get the channel acceptor script option.
sc, ok := initMsg.Options[channelAcceptScript]
if !ok {
c.sendError(
request.Id,
InvalidParams,
fmt.Sprintf("Missing option '%s'", channelAcceptScript),
)
return
}

c.channelAcceptScript, ok = sc.(string)
if !ok {
c.sendError(
request.Id,
InvalidParams,
fmt.Sprintf(
"Invalid value '%v' for option '%s'",
sc,
channelAcceptScript,
),
)
return
}

// Get the listen address option.
l, ok := initMsg.Options[ListenAddressOption]
if !ok {
Expand Down Expand Up @@ -382,6 +437,79 @@ func (c *ClnPlugin) handleHtlcAccepted(request *Request) {
c.server.Send(idToString(request.Id), &htlc)
}

func (c *ClnPlugin) handleSetChannelAcceptScript(request *Request) {
var params []string
err := json.Unmarshal(request.Params, &params)
if err != nil {
c.sendError(
request.Id,
ParseError,
fmt.Sprintf(
"Failed to unmarshal setchannelacceptscript params:%s [%s]",
err.Error(),
request.Params,
),
)
return
}
if len(params) >= 1 {
c.channelAcceptScript = params[0]
}
c.sendToCln(&Response{
JsonRpc: SpecVersion,
Id: request.Id,
Result: c.channelAcceptScript,
})
}

func unmarshalOpenChannel(request *Request) (r json.RawMessage, err error) {
switch request.Method {
case "openchannel":
var openChannel struct {
OpenChannel json.RawMessage `json:"openchannel"`
}
err = json.Unmarshal(request.Params, &openChannel)
if err != nil {
return
}
r = openChannel.OpenChannel
case "openchannel2":
var openChannel struct {
OpenChannel json.RawMessage `json:"openchannel2"`
}
err = json.Unmarshal(request.Params, &openChannel)
if err != nil {
return
}
r = openChannel.OpenChannel
}
return r, nil
}
func (c *ClnPlugin) handleOpenChannel(request *Request) {
p, err := unmarshalOpenChannel(request)
if err != nil {
c.sendError(
request.Id,
ParseError,
fmt.Sprintf(
"Failed to unmarshal openchannel params:%s [%s]",
err.Error(),
request.Params,
),
)
return
}
result, err := channelAcceptor(c.channelAcceptScript, request.Method, p)
if err != nil {
log.Printf("channelAcceptor error - request: %s error: %v", request, err)
}
c.sendToCln(&Response{
JsonRpc: SpecVersion,
Id: request.Id,
Result: result,
})
}

// Sends an error to cln.
func (c *ClnPlugin) sendError(id json.RawMessage, code int, message string) {
// Log the error to cln first.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require (
github.com/lightningnetwork/lnd/tlv v1.1.0
github.com/niftynei/glightning v0.8.2
github.com/stretchr/testify v1.8.1
go.starlark.net v0.0.0-20230612165344-9532f5667272
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
golang.org/x/sync v0.1.0
google.golang.org/grpc v1.50.1
Expand Down

0 comments on commit 70d6afc

Please sign in to comment.