Merge pull request #7668 from shaurya947/open-channel-memo

multi: accept memo note when opening channel
This commit is contained in:
Oliver Gugger 2023-05-19 16:13:13 +02:00 committed by GitHub
commit ac3e7be9a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2500 additions and 2363 deletions

View File

@ -220,6 +220,10 @@ const (
// A tlv type definition used to serialize and deserialize the
// confirmed ShortChannelID for a zero-conf channel.
realScidType tlv.Type = 4
// A tlv type definition used to serialize and deserialize the
// Memo for the channel channel.
channelMemoType tlv.Type = 5
)
// indexStatus is an enum-like type that describes what state the
@ -818,6 +822,10 @@ type OpenChannel struct {
// default ShortChannelID. This is only set for zero-conf channels.
confirmedScid lnwire.ShortChannelID
// Memo is any arbitrary information we wish to store locally about the
// channel that will be useful to our future selves.
Memo []byte
// TODO(roasbeef): eww
Db *ChannelStateDB
@ -3642,6 +3650,7 @@ func putChanInfo(chanBucket kvdb.RwBucket, channel *OpenChannel) error {
initialRemoteBalanceType, &remoteBalance,
),
MakeScidRecord(realScidType, &channel.confirmedScid),
tlv.MakePrimitiveRecord(channelMemoType, &channel.Memo),
)
if err != nil {
return err
@ -3839,10 +3848,11 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error {
}
}
// Create balance fields in uint64.
// Create balance fields in uint64, and Memo field as byte slice.
var (
localBalance uint64
remoteBalance uint64
memo []byte
)
// Create the tlv stream.
@ -3859,6 +3869,7 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error {
initialRemoteBalanceType, &remoteBalance,
),
MakeScidRecord(realScidType, &channel.confirmedScid),
tlv.MakePrimitiveRecord(channelMemoType, &memo),
)
if err != nil {
return err
@ -3872,6 +3883,11 @@ func fetchChanInfo(chanBucket kvdb.RBucket, channel *OpenChannel) error {
channel.InitialLocalBalance = lnwire.MilliSatoshi(localBalance)
channel.InitialRemoteBalance = lnwire.MilliSatoshi(remoteBalance)
// Attach the memo field if non-empty.
if len(memo) > 0 {
channel.Memo = memo
}
channel.Packager = NewChannelPackager(channel.ShortChannelID)
// Finally, read the optional shutdown scripts.

View File

@ -93,7 +93,12 @@ var openChannelCommand = cli.Command{
One can manually set the fee to be used for the funding transaction via
either the --conf_target or --sat_per_vbyte arguments. This is
optional.`,
optional.
One can also specify a short string memo to record some useful
information about the channel using the --memo argument. This is stored
locally only, and is purely for reference. It has no bearing on the
channel's operation. Max allowed length is 500 characters.`,
ArgsUsage: "node-key local-amt push-amt",
Flags: []cli.Flag{
cli.StringFlag{
@ -257,6 +262,14 @@ var openChannelCommand = cli.Command{
"payment. If not specified, a default of 1% " +
"of the channel capacity will be used.",
},
cli.StringFlag{
Name: "memo",
Usage: `(optional) a note-to-self containing some useful
information about the channel. This is stored
locally only, and is purely for reference. It
has no bearing on the channel's operation. Max
allowed length is 500 characters`,
},
},
Action: actionDecorator(openChannel),
}
@ -300,6 +313,7 @@ func openChannel(ctx *cli.Context) error {
ScidAlias: ctx.Bool("scid_alias"),
RemoteChanReserveSat: ctx.Uint64("remote_reserve_sats"),
FundMax: ctx.Bool("fundmax"),
Memo: ctx.String("memo"),
}
switch {

View File

@ -40,6 +40,11 @@ package](https://github.com/lightningnetwork/lnd/pull/7356)
and `--protocol.custom-invoice` flags to set feature bits for various feature
"sets", as defined in [BOLT 9](https://github.com/lightning/bolts/blob/master/09-features.md).
* `OpenChannel` now accepts an [optional `memo`
argument](https://github.com/lightningnetwork/lnd/pull/7668) for specifying
a helpful note-to-self containing arbitrary useful information about the
channel.
## Misc
* [Generate default macaroons

View File

@ -291,6 +291,10 @@ type InitFundingMsg struct {
// support explicit channel type negotiation.
ChannelType *lnwire.ChannelType
// Memo is any arbitrary information we wish to store locally about the
// channel that will be useful to our future selves.
Memo []byte
// Updates is a channel which updates to the opening status of the
// channel are sent on.
Updates chan *lnrpc.OpenStatusUpdate
@ -4137,6 +4141,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
ZeroConf: zeroConf,
OptionScidAlias: scid,
ScidAliasFeature: scidFeatureVal,
Memo: msg.Memo,
}
reservation, err := f.cfg.Wallet.InitChannelReservation(req)

View File

@ -2,6 +2,7 @@ package itest
import (
"fmt"
"strings"
"testing"
"github.com/btcsuite/btcd/btcjson"
@ -373,16 +374,32 @@ func runBasicChannelCreationAndUpdates(ht *lntest.HarnessTest,
aliceChanSub := alice.RPC.SubscribeChannelEvents()
// Open the channels between Alice and Bob, asserting that the channels
// have been properly opened on-chain.
// have been properly opened on-chain. We also attach the optional Memo
// argument to one of the channels so we can test that it can be
// retrieved correctly when querying the created channel.
chanPoints := make([]*lnrpc.ChannelPoint, numChannels)
openChannelParams := []lntest.OpenChannelParams{
{Amt: amount, Memo: "bob is a good peer"},
{Amt: amount},
}
for i := 0; i < numChannels; i++ {
chanPoints[i] = ht.OpenChannel(
alice, bob, lntest.OpenChannelParams{
Amt: amount,
},
alice, bob, openChannelParams[i],
)
}
// Alice should see the memo when retrieving the first channel.
channel := ht.QueryChannelByChanPoint(alice, chanPoints[0])
require.Equal(ht, "bob is a good peer", channel.Memo)
// Bob shouldn't see the memo since it's for Alice only.
channel = ht.QueryChannelByChanPoint(bob, chanPoints[0])
require.Empty(ht, channel.Memo, "Memo is not empty")
// The second channel doesn't have a memo.
channel = ht.QueryChannelByChanPoint(alice, chanPoints[1])
require.Empty(ht, channel.Memo, "Memo is not empty")
// Since each of the channels just became open, Bob and Alice should
// each receive an open and an active notification for each channel.
const numExpectedOpenUpdates = 3 * numChannels
@ -445,6 +462,16 @@ func runBasicChannelCreationAndUpdates(ht *lntest.HarnessTest,
}
}
// If Bob now tries to open a channel with an invalid memo, reject it.
invalidMemo := strings.Repeat("a", 501)
params := lntest.OpenChannelParams{
Amt: funding.MaxBtcFundingAmount,
Memo: invalidMemo,
}
expErr := fmt.Errorf("provided memo (%s) is of length 501, exceeds 500",
invalidMemo)
ht.OpenChannelAssertErr(bob, alice, params, expErr)
// verifyCloseUpdatesReceived is used to verify that Alice and Bob
// receive the correct channel updates in order.
const numExpectedCloseUpdates = 3 * numChannels

File diff suppressed because it is too large Load Diff

View File

@ -1545,6 +1545,13 @@ message Channel {
// This is the peer SCID alias.
uint64 peer_scid_alias = 35 [jstype = JS_STRING];
/*
An optional note-to-self to go along with the channel containing some
useful information. This is only ever stored locally and in no way impacts
the channel's operation.
*/
string memo = 36;
}
message ListChannelsRequest {
@ -2274,6 +2281,13 @@ message OpenChannelRequest {
be zero and is ignored.
*/
bool fund_max = 26;
/*
An optional note-to-self to go along with the channel containing some
useful information. This is only ever stored locally and in no way impacts
the channel's operation.
*/
string memo = 27;
}
message OpenStatusUpdate {
oneof update {

View File

@ -3573,6 +3573,10 @@
"type": "string",
"format": "uint64",
"description": "This is the peer SCID alias."
},
"memo": {
"type": "string",
"description": "An optional note-to-self to go along with the channel containing some\nuseful information. This is only ever stored locally and in no way impacts\nthe channel's operation."
}
}
},
@ -5820,6 +5824,10 @@
"fund_max": {
"type": "boolean",
"description": "If set, then lnd will attempt to commit all the coins under control of the\ninternal wallet to open the channel, and the LocalFundingAmount field must\nbe zero and is ignored."
},
"memo": {
"type": "string",
"description": "An optional note-to-self to go along with the channel containing some\nuseful information. This is only ever stored locally and in no way impacts\nthe channel's operation."
}
}
},

View File

@ -890,6 +890,12 @@ type OpenChannelParams struct {
// FundMax is a boolean indicating whether the channel should be funded
// with the maximum possible amount from the wallet.
FundMax bool
// An optional note-to-self containing some useful information about the
// channel. This is stored locally only, and is purely for reference. It
// has no bearing on the channel's operation. Max allowed length is 500
// characters.
Memo string
}
// prepareOpenChannel waits for both nodes to be synced to chain and returns an
@ -931,6 +937,7 @@ func (h *HarnessTest) prepareOpenChannel(srcNode, destNode *node.HarnessNode,
UseBaseFee: p.UseBaseFee,
UseFeeRate: p.UseFeeRate,
FundMax: p.FundMax,
Memo: p.Memo,
}
}

View File

@ -416,6 +416,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
Db: wallet.Cfg.Database,
InitialLocalBalance: ourBalance,
InitialRemoteBalance: theirBalance,
Memo: req.Memo,
},
pushMSat: req.PushMSat,
pendingChanID: req.PendingChanID,

View File

@ -179,6 +179,10 @@ type InitFundingReserveMsg struct {
// negotiated.
ScidAliasFeature bool
// Memo is any arbitrary information we wish to store locally about the
// channel that will be useful to our future selves.
Memo []byte
// err is a channel in which all errors will be sent across. Will be
// nil if this initial set is successful.
//

View File

@ -2169,6 +2169,15 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
in.CommitmentType)
}
// We limit the channel memo to be 500 characters long. This enforces
// a reasonable upper bound on storage consumption. This also mimics
// the length limit for the label of a TX.
const maxMemoLength = 500
if len(in.Memo) > maxMemoLength {
return nil, fmt.Errorf("provided memo (%s) is of length %d, "+
"exceeds %d", in.Memo, len(in.Memo), maxMemoLength)
}
// Instruct the server to trigger the necessary events to attempt to
// open a new channel. A stream is returned in place, this stream will
// be used to consume updates of the state of the pending channel.
@ -2194,6 +2203,7 @@ func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
ChannelType: channelType,
FundUpToMaxAmt: fundUpToMaxAmt,
MinFundAmt: minFundAmt,
Memo: []byte(in.Memo),
}, nil
}
@ -4214,6 +4224,7 @@ func createRPCOpenChannel(r *rpcServer, dbChannel *channeldb.OpenChannel,
PeerScidAlias: peerScidAlias.ToUint64(),
ZeroConf: dbChannel.IsZeroConf(),
ZeroConfConfirmedScid: dbChannel.ZeroConfRealScid().ToUint64(),
Memo: string(dbChannel.Memo),
// TODO: remove the following deprecated fields
CsvDelay: uint32(dbChannel.LocalChanCfg.CsvDelay),
LocalChanReserveSat: int64(dbChannel.LocalChanCfg.ChanReserve),