multi: add co-op close custom data to close update

With this commit we populate additional information about the close
outputs (including potential custom channel data) in the close update
RPC message.
This will allow custom channels to find out how the additional close
outputs look like on chain and what data they might commit to.

We also hook up the aux custom data formatter, so it can format the
custom channel data to JSON.
This commit is contained in:
Oliver Gugger 2024-05-29 19:57:49 +02:00
parent 39e4e8d8a4
commit 9ce799578c
No known key found for this signature in database
GPG key ID: 8E4256593F177720
5 changed files with 3624 additions and 3308 deletions

File diff suppressed because it is too large Load diff

View file

@ -2033,10 +2033,38 @@ message ChannelOpenUpdate {
ChannelPoint channel_point = 1;
}
message CloseOutput {
// The amount in satoshi of this close output. This amount is the final
// commitment balance of the channel and the actual amount paid out on chain
// might be smaller due to subtracted fees.
int64 amount_sat = 1;
// The pkScript of the close output.
bytes pk_script = 2;
// Whether this output is for the local or remote node.
bool is_local = 3;
// The TLV encoded custom channel data records for this output, which might
// be set for custom channels.
bytes custom_channel_data = 4;
}
message ChannelCloseUpdate {
bytes closing_txid = 1;
bool success = 2;
// The local channel close output. If the local channel balance was dust to
// begin with, this output will not be set.
CloseOutput local_close_output = 3;
// The remote channel close output. If the remote channel balance was dust
// to begin with, this output will not be set.
CloseOutput remote_close_output = 4;
// Any additional outputs that might be added for custom channel types.
repeated CloseOutput additional_outputs = 5;
}
message CloseChannelRequest {

View file

@ -4156,6 +4156,21 @@
},
"success": {
"type": "boolean"
},
"local_close_output": {
"$ref": "#/definitions/lnrpcCloseOutput",
"description": "The local channel close output. If the local channel balance was dust to\nbegin with, this output will not be set."
},
"remote_close_output": {
"$ref": "#/definitions/lnrpcCloseOutput",
"description": "The remote channel close output. If the remote channel balance was dust\nto begin with, this output will not be set."
},
"additional_outputs": {
"type": "array",
"items": {
"$ref": "#/definitions/lnrpcCloseOutput"
},
"description": "Any additional outputs that might be added for custom channel types."
}
}
},
@ -4465,6 +4480,30 @@
}
}
},
"lnrpcCloseOutput": {
"type": "object",
"properties": {
"amount_sat": {
"type": "string",
"format": "int64",
"description": "The amount in satoshi of this close output. This amount is the final\ncommitment balance of the channel and the actual amount paid out on chain\nmight be smaller due to subtracted fees."
},
"pk_script": {
"type": "string",
"format": "byte",
"description": "The pkScript of the close output."
},
"is_local": {
"type": "boolean",
"description": "Whether this output is for the local or remote node."
},
"custom_channel_data": {
"type": "string",
"format": "byte",
"description": "The TLV encoded custom channel data records for this output, which might\nbe set for custom channels."
}
}
},
"lnrpcCloseStatusUpdate": {
"type": "object",
"properties": {

View file

@ -145,6 +145,21 @@ type PendingUpdate struct {
type ChannelCloseUpdate struct {
ClosingTxid []byte
Success bool
// LocalCloseOutput is an optional, additional output on the closing
// transaction that the local party should be paid to. This will only be
// populated if the local balance isn't dust.
LocalCloseOutput fn.Option[chancloser.CloseOutput]
// RemoteCloseOutput is an optional, additional output on the closing
// transaction that the remote party should be paid to. This will only
// be populated if the remote balance isn't dust.
RemoteCloseOutput fn.Option[chancloser.CloseOutput]
// AuxOutputs is an optional set of additional outputs that might be
// included in the closing transaction. These are used for custom
// channel types.
AuxOutputs fn.Option[chancloser.AuxCloseOutputs]
}
// TimestampedError is a timestamped error that is used to store the most recent
@ -3567,17 +3582,25 @@ func (p *Brontide) finalizeChanClosure(chanCloser *chancloser.ChanCloser) {
}
}
go WaitForChanToClose(chanCloser.NegotiationHeight(), notifier, errChan,
localOut := chanCloser.LocalCloseOutput()
remoteOut := chanCloser.RemoteCloseOutput()
auxOut := chanCloser.AuxOutputs()
go WaitForChanToClose(
chanCloser.NegotiationHeight(), notifier, errChan,
&chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() {
// Respond to the local subsystem which requested the
// channel closure.
if closeReq != nil {
closeReq.Updates <- &ChannelCloseUpdate{
ClosingTxid: closingTxid[:],
Success: true,
ClosingTxid: closingTxid[:],
Success: true,
LocalCloseOutput: localOut,
RemoteCloseOutput: remoteOut,
AuxOutputs: auxOut,
}
}
})
},
)
}
// WaitForChanToClose uses the passed notifier to wait until the channel has

View file

@ -2685,7 +2685,6 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
// transaction here rather than going to the switch as we don't require
// interaction from the peer.
if force {
// As we're force closing this channel, as a precaution, we'll
// ensure that the switch doesn't continue to see this channel
// as eligible for forwarding HTLC's. If the peer is online,
@ -2725,15 +2724,19 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
errChan = make(chan error, 1)
notifier := r.server.cc.ChainNotifier
go peer.WaitForChanToClose(uint32(bestHeight), notifier, errChan, chanPoint,
go peer.WaitForChanToClose(
uint32(bestHeight), notifier, errChan, chanPoint,
&closingTxid, closingTx.TxOut[0].PkScript, func() {
// Respond to the local subsystem which
// requested the channel closure.
updateChan <- &peer.ChannelCloseUpdate{
ClosingTxid: closingTxid[:],
Success: true,
// Force closure transactions don't have
// additional local/remote outputs.
}
})
},
)
} else {
// If this is a frozen channel, then we only allow the co-op
// close to proceed if we were the responder to this channel if
@ -2857,6 +2860,19 @@ out:
return err
}
err = fn.MapOptionZ(
r.server.implCfg.AuxDataParser,
func(parser AuxDataParser) error {
return parser.InlineParseCustomData(
rpcClosingUpdate,
)
},
)
if err != nil {
return fmt.Errorf("error parsing custom data: "+
"%w", err)
}
rpcsLog.Tracef("[closechannel] sending update: %v",
rpcClosingUpdate)
@ -2882,19 +2898,84 @@ out:
return nil
}
func createRPCCloseUpdate(update interface{}) (
*lnrpc.CloseStatusUpdate, error) {
func createRPCCloseUpdate(
update interface{}) (*lnrpc.CloseStatusUpdate, error) {
switch u := update.(type) {
case *peer.ChannelCloseUpdate:
ccu := &lnrpc.ChannelCloseUpdate{
ClosingTxid: u.ClosingTxid,
Success: u.Success,
}
err := fn.MapOptionZ(
u.LocalCloseOutput,
func(closeOut chancloser.CloseOutput) error {
cr, err := closeOut.ShutdownRecords.Serialize()
if err != nil {
return fmt.Errorf("error serializing "+
"local close out custom "+
"records: %w", err)
}
rpcCloseOut := &lnrpc.CloseOutput{
AmountSat: int64(closeOut.Amt),
PkScript: closeOut.PkScript,
IsLocal: true,
CustomChannelData: cr,
}
ccu.LocalCloseOutput = rpcCloseOut
return nil
},
)
if err != nil {
return nil, err
}
err = fn.MapOptionZ(
u.RemoteCloseOutput,
func(closeOut chancloser.CloseOutput) error {
cr, err := closeOut.ShutdownRecords.Serialize()
if err != nil {
return fmt.Errorf("error serializing "+
"remote close out custom "+
"records: %w", err)
}
rpcCloseOut := &lnrpc.CloseOutput{
AmountSat: int64(closeOut.Amt),
PkScript: closeOut.PkScript,
CustomChannelData: cr,
}
ccu.RemoteCloseOutput = rpcCloseOut
return nil
},
)
if err != nil {
return nil, err
}
u.AuxOutputs.WhenSome(func(outs chancloser.AuxCloseOutputs) {
for _, out := range outs.ExtraCloseOutputs {
ccu.AdditionalOutputs = append(
ccu.AdditionalOutputs,
&lnrpc.CloseOutput{
AmountSat: out.Value,
PkScript: out.PkScript,
IsLocal: out.IsLocal,
},
)
}
})
return &lnrpc.CloseStatusUpdate{
Update: &lnrpc.CloseStatusUpdate_ChanClose{
ChanClose: &lnrpc.ChannelCloseUpdate{
ClosingTxid: u.ClosingTxid,
Success: u.Success,
},
ChanClose: ccu,
},
}, nil
case *peer.PendingUpdate:
return &lnrpc.CloseStatusUpdate{
Update: &lnrpc.CloseStatusUpdate_ClosePending{