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 committed 2024-09-19 10:18:41 +02:00
1 parent 8dee76a1b8
commit e0b4601fb9
5 files changed
+3624 -3308

No files matched your search

+3440 -3295
View File
File diff suppressed because it is too large. Load diff
+28
View File
@@ -2033,10 +2033,38 @@ message ChannelOpenUpdate {
ChannelPoint channel_point = 1; 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 { message ChannelCloseUpdate {
bytes closing_txid = 1; bytes closing_txid = 1;
bool success = 2; 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 { message CloseChannelRequest {
+39
View File
@@ -4156,6 +4156,21 @@
}, },
"success": { "success": {
"type": "boolean" "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": { "lnrpcCloseStatusUpdate": {
"type": "object", "type": "object",
"properties": { "properties": {
+27 -4
View File
@@ -145,6 +145,21 @@ type PendingUpdate struct {
type ChannelCloseUpdate struct { type ChannelCloseUpdate struct {
ClosingTxid []byte ClosingTxid []byte
Success bool 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 // 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() { &chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() {
// Respond to the local subsystem which requested the // Respond to the local subsystem which requested the
// channel closure. // channel closure.
if closeReq != nil { if closeReq != nil {
closeReq.Updates <- &ChannelCloseUpdate{ closeReq.Updates <- &ChannelCloseUpdate{
ClosingTxid: closingTxid[:], ClosingTxid: closingTxid[:],
Success: true, Success: true,
LocalCloseOutput: localOut,
RemoteCloseOutput: remoteOut,
AuxOutputs: auxOut,
} }
} }
}) },
)
} }
// WaitForChanToClose uses the passed notifier to wait until the channel has // WaitForChanToClose uses the passed notifier to wait until the channel has
+90 -9
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 // transaction here rather than going to the switch as we don't require
// interaction from the peer. // interaction from the peer.
if force { if force {
// As we're force closing this channel, as a precaution, we'll // As we're force closing this channel, as a precaution, we'll
// ensure that the switch doesn't continue to see this channel // ensure that the switch doesn't continue to see this channel
// as eligible for forwarding HTLC's. If the peer is online, // 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) errChan = make(chan error, 1)
notifier := r.server.cc.ChainNotifier 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() { &closingTxid, closingTx.TxOut[0].PkScript, func() {
// Respond to the local subsystem which // Respond to the local subsystem which
// requested the channel closure. // requested the channel closure.
updateChan <- &peer.ChannelCloseUpdate{ updateChan <- &peer.ChannelCloseUpdate{
ClosingTxid: closingTxid[:], ClosingTxid: closingTxid[:],
Success: true, Success: true,
// Force closure transactions don't have
// additional local/remote outputs.
} }
}) },
)
} else { } else {
// If this is a frozen channel, then we only allow the co-op // 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 // close to proceed if we were the responder to this channel if
@@ -2857,6 +2860,19 @@ out:
return err 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", rpcsLog.Tracef("[closechannel] sending update: %v",
rpcClosingUpdate) rpcClosingUpdate)
@@ -2882,19 +2898,84 @@ out:
return nil return nil
} }
func createRPCCloseUpdate(update interface{}) ( func createRPCCloseUpdate(
*lnrpc.CloseStatusUpdate, error) { update interface{}) (*lnrpc.CloseStatusUpdate, error) {
switch u := update.(type) { switch u := update.(type) {
case *peer.ChannelCloseUpdate: 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{ return &lnrpc.CloseStatusUpdate{
Update: &lnrpc.CloseStatusUpdate_ChanClose{ Update: &lnrpc.CloseStatusUpdate_ChanClose{
ChanClose: &lnrpc.ChannelCloseUpdate{ ChanClose: ccu,
ClosingTxid: u.ClosingTxid,
Success: u.Success,
},
}, },
}, nil }, nil
case *peer.PendingUpdate: case *peer.PendingUpdate:
return &lnrpc.CloseStatusUpdate{ return &lnrpc.CloseStatusUpdate{
Update: &lnrpc.CloseStatusUpdate_ClosePending{ Update: &lnrpc.CloseStatusUpdate_ClosePending{