Skip to content
This repository has been archived by the owner on Sep 2, 2024. It is now read-only.

Commit

Permalink
feat: update channel (#457)
Browse files Browse the repository at this point in the history
Co-authored-by: Adithya Vardhan <[email protected]>
  • Loading branch information
rolznz and im-adithya authored Jun 18, 2024
1 parent 283f09f commit 93d70f6
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 7 deletions.
10 changes: 10 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,16 @@ func (api *api) CloseChannel(ctx context.Context, peerId, channelId string, forc
})
}

func (api *api) UpdateChannel(ctx context.Context, updateChannelRequest *UpdateChannelRequest) error {
if api.svc.GetLNClient() == nil {
return errors.New("LNClient not started")
}
logger.Logger.WithFields(logrus.Fields{
"request": updateChannelRequest,
}).Info("updating channel")
return api.svc.GetLNClient().UpdateChannel(ctx, updateChannelRequest)
}

func (api *api) GetNewOnchainAddress(ctx context.Context) (string, error) {
if api.svc.GetLNClient() == nil {
return "", errors.New("LNClient not started")
Expand Down
2 changes: 2 additions & 0 deletions api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type API interface {
DisconnectPeer(ctx context.Context, peerId string) error
OpenChannel(ctx context.Context, openChannelRequest *OpenChannelRequest) (*OpenChannelResponse, error)
CloseChannel(ctx context.Context, peerId, channelId string, force bool) (*CloseChannelResponse, error)
UpdateChannel(ctx context.Context, updateChannelRequest *UpdateChannelRequest) error
GetNewOnchainAddress(ctx context.Context) (string, error)
GetUnusedOnchainAddress(ctx context.Context) (string, error)
SignMessage(ctx context.Context, message string) (*SignMessageResponse, error)
Expand Down Expand Up @@ -163,6 +164,7 @@ type ConnectPeerRequest = lnclient.ConnectPeerRequest
type OpenChannelRequest = lnclient.OpenChannelRequest
type OpenChannelResponse = lnclient.OpenChannelResponse
type CloseChannelResponse = lnclient.CloseChannelResponse
type UpdateChannelRequest = lnclient.UpdateChannelRequest

type RedeemOnchainFundsRequest struct {
ToAddress string `json:"toAddress"`
Expand Down
59 changes: 58 additions & 1 deletion frontend/src/screens/channels/Channels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ChevronDown,
CopyIcon,
ExternalLinkIcon,
HandCoins,
Hotel,
MoreHorizontal,
Trash2,
Expand Down Expand Up @@ -53,7 +54,12 @@ import { useRedeemOnchainFunds } from "src/hooks/useRedeemOnchainFunds.ts";
import { useSyncWallet } from "src/hooks/useSyncWallet.ts";
import { copyToClipboard } from "src/lib/clipboard.ts";
import { formatAmount } from "src/lib/utils.ts";
import { CloseChannelResponse, Node } from "src/types";
import {
Channel,
CloseChannelResponse,
Node,
UpdateChannelRequest,
} from "src/types";
import { request } from "src/utils/request";
import { useCSRF } from "../../hooks/useCSRF.ts";

Expand Down Expand Up @@ -169,6 +175,48 @@ export default function Channels() {
}
}

async function editChannel(channel: Channel) {
try {
if (!csrf) {
throw new Error("csrf not loaded");
}

const forwardingFeeBaseSats = prompt(
"Enter base forwarding fee in sats",
Math.floor(channel.forwardingFeeBaseMsat / 1000).toString()
);

if (!forwardingFeeBaseSats) {
return;
}

const forwardingFeeBaseMsat = +forwardingFeeBaseSats * 1000;

console.info(
`🎬 Updating channel ${channel.id} with ${channel.remotePubkey}`
);

await request(
`/api/peers/${channel.remotePubkey}/channels/${channel.id}`,
{
method: "PATCH",
headers: {
"X-CSRF-Token": csrf,
"Content-Type": "application/json",
},
body: JSON.stringify({
forwardingFeeBaseMsat: forwardingFeeBaseMsat,
} as UpdateChannelRequest),
}
);
await reloadChannels();
toast({ title: "Sucessfully updated channel" });
} catch (error) {
console.error(error);
alert("Something went wrong: " + error);
}
}

async function resetRouter() {
try {
if (!csrf) {
Expand Down Expand Up @@ -480,6 +528,15 @@ export default function Channels() {
<p>View Funding Transaction</p>
</ExternalLink>
</DropdownMenuItem>
{channel.public && (
<DropdownMenuItem
className="flex flex-row items-center gap-2 cursor-pointer"
onClick={() => editChannel(channel)}
>
<HandCoins className="h-4 w-4" />
Set Routing Fee
</DropdownMenuItem>
)}
<DropdownMenuItem
className="flex flex-row items-center gap-2 cursor-pointer"
onClick={() =>
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,11 @@ export type Channel = {
public: boolean;
confirmations?: number;
confirmationsRequired?: number;
forwardingFeeBaseMsat: number;
};

export type UpdateChannelRequest = {
forwardingFeeBaseMsat: number;
};

export type Peer = {
Expand Down
25 changes: 25 additions & 0 deletions http/http_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func (httpSvc *HttpService) RegisterSharedRoutes(e *echo.Echo) {
e.POST("/api/peers", httpSvc.connectPeerHandler, authMiddleware)
e.DELETE("/api/peers/:peerId", httpSvc.disconnectPeerHandler, authMiddleware)
e.DELETE("/api/peers/:peerId/channels/:channelId", httpSvc.closeChannelHandler, authMiddleware)
e.PATCH("/api/peers/:peerId/channels/:channelId", httpSvc.updateChannelHandler, authMiddleware)
e.GET("/api/wallet/address", httpSvc.onchainAddressHandler, authMiddleware)
e.POST("/api/wallet/new-address", httpSvc.newOnchainAddressHandler, authMiddleware)
e.POST("/api/wallet/redeem-onchain-funds", httpSvc.redeemOnchainFundsHandler, authMiddleware)
Expand Down Expand Up @@ -498,6 +499,30 @@ func (httpSvc *HttpService) closeChannelHandler(c echo.Context) error {
return c.JSON(http.StatusOK, closeChannelResponse)
}

func (httpSvc *HttpService) updateChannelHandler(c echo.Context) error {
ctx := c.Request().Context()

var updateChannelRequest api.UpdateChannelRequest
if err := c.Bind(&updateChannelRequest); err != nil {
return c.JSON(http.StatusBadRequest, ErrorResponse{
Message: fmt.Sprintf("Bad request: %s", err.Error()),
})
}

updateChannelRequest.NodeId = c.Param("peerId")
updateChannelRequest.ChannelId = c.Param("channelId")

err := httpSvc.api.UpdateChannel(ctx, &updateChannelRequest)

if err != nil {
return c.JSON(http.StatusInternalServerError, ErrorResponse{
Message: fmt.Sprintf("Failed to update channel: %s", err.Error()),
})
}

return c.NoContent(http.StatusNoContent)
}

func (httpSvc *HttpService) newInstantChannelInvoiceHandler(c echo.Context) error {
ctx := c.Request().Context()

Expand Down
4 changes: 4 additions & 0 deletions lnclient/breez/breez.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,10 @@ func (bs *BreezService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraph

func (bs *BreezService) UpdateLastWalletSyncRequest() {}

func (bs *BreezService) UpdateChannel(ctx context.Context, updateChannelRequest *lnclient.UpdateChannelRequest) error {
return nil
}

func (bs *BreezService) DisconnectPeer(ctx context.Context, peerId string) error {
return nil
}
5 changes: 5 additions & 0 deletions lnclient/cashu/cashu.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,15 @@ func (cs *CashuService) GetNodeStatus(ctx context.Context) (nodeStatus *lnclient
func (cs *CashuService) SendPaymentProbes(ctx context.Context, invoice string) error {
return nil
}

func (cs *CashuService) SendSpontaneousPaymentProbes(ctx context.Context, amountMsat uint64, nodeId string) error {
return nil
}

func (cs *CashuService) UpdateChannel(ctx context.Context, updateChannelRequest *lnclient.UpdateChannelRequest) error {
return nil
}

func (cs *CashuService) GetBalances(ctx context.Context) (*lnclient.BalancesResponse, error) {
balance, err := cs.GetBalance(ctx)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions lnclient/greenlight/greenlight.go
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,10 @@ func (gs *GreenlightService) GetNetworkGraph(nodeIds []string) (lnclient.Network

func (gs *GreenlightService) UpdateLastWalletSyncRequest() {}

func (gs *GreenlightService) UpdateChannel(ctx context.Context, updateChannelRequest *lnclient.UpdateChannelRequest) error {
return nil
}

func (gs *GreenlightService) DisconnectPeer(ctx context.Context, peerId string) error {
return nil
}
46 changes: 44 additions & 2 deletions lnclient/ldk/ldk.go
Original file line number Diff line number Diff line change
Expand Up @@ -781,8 +781,18 @@ func (ls *LDKService) ListChannels(ctx context.Context) ([]lnclient.Channel, err
fundingTxId = ldkChannel.FundingTxo.Txid
}

internalChannel := map[string]interface{}{}
internalChannel["channel"] = ldkChannel
internalChannel["config"] = map[string]interface{}{
"AcceptUnderpayingHtlcs": ldkChannel.Config.AcceptUnderpayingHtlcs(),
"CltvExpiryDelta": ldkChannel.Config.CltvExpiryDelta(),
"ForceCloseAvoidanceMaxFeeSatoshis": ldkChannel.Config.ForceCloseAvoidanceMaxFeeSatoshis(),
"ForwardingFeeBaseMsat": ldkChannel.Config.ForwardingFeeBaseMsat(),
"ForwardingFeeProportionalMillionths": ldkChannel.Config.ForwardingFeeProportionalMillionths(),
}

channels = append(channels, lnclient.Channel{
InternalChannel: ldkChannel,
InternalChannel: internalChannel,
LocalBalance: int64(ldkChannel.OutboundCapacityMsat),
RemoteBalance: int64(ldkChannel.InboundCapacityMsat),
RemotePubkey: ldkChannel.CounterpartyNodeId,
Expand All @@ -792,6 +802,7 @@ func (ls *LDKService) ListChannels(ctx context.Context) ([]lnclient.Channel, err
FundingTxId: fundingTxId,
Confirmations: ldkChannel.Confirmations,
ConfirmationsRequired: ldkChannel.ConfirmationsRequired,
ForwardingFeeBaseMsat: ldkChannel.Config.ForwardingFeeBaseMsat(),
})
}

Expand Down Expand Up @@ -849,8 +860,13 @@ func (ls *LDKService) OpenChannel(ctx context.Context, openChannelRequest *lncli
ldkEventSubscription := ls.ldkEventBroadcaster.Subscribe()
defer ls.ldkEventBroadcaster.CancelSubscription(ldkEventSubscription)

channelConfig := ldk_node.NewChannelConfig()

// set a super-high forwarding fee of 100K sats by default to disable unwanted routing
channelConfig.SetForwardingFeeBaseMsat(100_000_000)

logger.Logger.WithField("peer_id", foundPeer.NodeId).Info("Opening channel")
userChannelId, err := ls.node.ConnectOpenChannel(foundPeer.NodeId, foundPeer.Address, uint64(openChannelRequest.Amount), nil, nil, openChannelRequest.Public)
userChannelId, err := ls.node.ConnectOpenChannel(foundPeer.NodeId, foundPeer.Address, uint64(openChannelRequest.Amount), nil, &channelConfig, openChannelRequest.Public)
if err != nil {
logger.Logger.WithError(err).Error("OpenChannel failed")
return nil, err
Expand Down Expand Up @@ -890,6 +906,32 @@ func (ls *LDKService) OpenChannel(ctx context.Context, openChannelRequest *lncli
return nil, errors.New("open channel timeout")
}

func (ls *LDKService) UpdateChannel(ctx context.Context, updateChannelRequest *lnclient.UpdateChannelRequest) error {
channels := ls.node.ListChannels()

var foundChannel *ldk_node.ChannelDetails
for _, channel := range channels {
if channel.UserChannelId == updateChannelRequest.ChannelId && channel.CounterpartyNodeId == updateChannelRequest.NodeId {
foundChannel = &channel
break
}
}

if foundChannel == nil {
return errors.New("channel not found")
}

existingConfig := foundChannel.Config
existingConfig.SetForwardingFeeBaseMsat(updateChannelRequest.ForwardingFeeBaseMsat)

err := ls.node.UpdateChannelConfig(updateChannelRequest.ChannelId, updateChannelRequest.NodeId, existingConfig)
if err != nil {
logger.Logger.WithError(err).Error("UpdateChannelConfig failed")
return err
}
return nil
}

func (ls *LDKService) CloseChannel(ctx context.Context, closeChannelRequest *lnclient.CloseChannelRequest) (*lnclient.CloseChannelResponse, error) {
logger.Logger.WithFields(logrus.Fields{
"request": closeChannelRequest,
Expand Down
48 changes: 48 additions & 0 deletions lnclient/lnd/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ func (svc *LNDService) OpenChannel(ctx context.Context, openChannelRequest *lncl
NodePubkey: nodePub,
Private: !openChannelRequest.Public,
LocalFundingAmount: openChannelRequest.Amount,
// set a super-high forwarding fee of 100K sats by default to disable unwanted routing
BaseFee: 100_000_000,
})
if err != nil {
return nil, fmt.Errorf("failed to open channel with %s: %s", foundPeer.NodeId, err)
Expand Down Expand Up @@ -741,6 +743,52 @@ func (svc *LNDService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGraphR

func (svc *LNDService) UpdateLastWalletSyncRequest() {}

func (svc *LNDService) UpdateChannel(ctx context.Context, updateChannelRequest *lnclient.UpdateChannelRequest) error {
logger.Logger.WithFields(logrus.Fields{
"request": updateChannelRequest,
}).Info("Updating Channel")

chanId64, err := strconv.ParseUint(updateChannelRequest.ChannelId, 10, 64)
if err != nil {
return err
}

channelEdge, err := svc.client.GetChanInfo(ctx, &lnrpc.ChanInfoRequest{
ChanId: chanId64,
})
if err != nil {
return err
}

channelPoint, err := svc.parseChannelPoint(channelEdge.ChanPoint)
if err != nil {
return err
}

var nodePolicy *lnrpc.RoutingPolicy
if channelEdge.Node1Pub == svc.client.IdentityPubkey {
nodePolicy = channelEdge.Node1Policy
} else {
nodePolicy = channelEdge.Node2Policy
}

_, err = svc.client.UpdateChannel(ctx, &lnrpc.PolicyUpdateRequest{
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: channelPoint,
},
BaseFeeMsat: int64(updateChannelRequest.ForwardingFeeBaseMsat),
FeeRatePpm: uint32(nodePolicy.FeeRateMilliMsat),
TimeLockDelta: nodePolicy.TimeLockDelta,
MaxHtlcMsat: nodePolicy.MaxHtlcMsat,
})

if err != nil {
return err
}

return nil
}

func (svc *LNDService) DisconnectPeer(ctx context.Context, peerId string) error {
return nil
}
8 changes: 8 additions & 0 deletions lnclient/lnd/wrapper/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,11 @@ func (wrapper *LNDWrapper) WalletBalance(ctx context.Context, req *lnrpc.WalletB
func (wrapper *LNDWrapper) NewAddress(ctx context.Context, req *lnrpc.NewAddressRequest, options ...grpc.CallOption) (*lnrpc.NewAddressResponse, error) {
return wrapper.client.NewAddress(ctx, req, options...)
}

func (wrapper *LNDWrapper) GetChanInfo(ctx context.Context, req *lnrpc.ChanInfoRequest, options ...grpc.CallOption) (*lnrpc.ChannelEdge, error) {
return wrapper.client.GetChanInfo(ctx, req, options...)
}

func (wrapper *LNDWrapper) UpdateChannel(ctx context.Context, req *lnrpc.PolicyUpdateRequest, options ...grpc.CallOption) (*lnrpc.PolicyUpdateResponse, error) {
return wrapper.client.UpdateChannelPolicy(ctx, req, options...)
}
8 changes: 8 additions & 0 deletions lnclient/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type LNClient interface {
ConnectPeer(ctx context.Context, connectPeerRequest *ConnectPeerRequest) error
OpenChannel(ctx context.Context, openChannelRequest *OpenChannelRequest) (*OpenChannelResponse, error)
CloseChannel(ctx context.Context, closeChannelRequest *CloseChannelRequest) (*CloseChannelResponse, error)
UpdateChannel(ctx context.Context, updateChannelRequest *UpdateChannelRequest) error
DisconnectPeer(ctx context.Context, peerId string) error
GetNewOnchainAddress(ctx context.Context) (string, error)
ResetRouter(key string) error
Expand Down Expand Up @@ -83,6 +84,7 @@ type Channel struct {
InternalChannel interface{} `json:"internalChannel"`
Confirmations *uint32 `json:"confirmations"`
ConfirmationsRequired *uint32 `json:"confirmationsRequired"`
ForwardingFeeBaseMsat uint32 `json:"forwardingFeeBaseMsat"`
}

type NodeStatus struct {
Expand Down Expand Up @@ -111,6 +113,12 @@ type CloseChannelRequest struct {
Force bool `json:"force"`
}

type UpdateChannelRequest struct {
ChannelId string `json:"channelId"`
NodeId string `json:"nodeId"`
ForwardingFeeBaseMsat uint32 `json:"forwardingFeeBaseMsat"`
}

type CloseChannelResponse struct {
}

Expand Down
5 changes: 5 additions & 0 deletions lnclient/phoenixd/phoenixd.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,11 @@ func (svc *PhoenixService) GetNetworkGraph(nodeIds []string) (lnclient.NetworkGr
}

func (svc *PhoenixService) UpdateLastWalletSyncRequest() {}

func (svc *PhoenixService) DisconnectPeer(ctx context.Context, peerId string) error {
return nil
}

func (svc *PhoenixService) UpdateChannel(ctx context.Context, updateChannelRequest *lnclient.UpdateChannelRequest) error {
return nil
}
Loading

0 comments on commit 93d70f6

Please sign in to comment.