multi: add blinding point to payment descriptor and persist

This commit adds an optional blinding point to payment descriptors and
persists them in our HTLC's extra data. A get/set pattern is used to
populate the ExtraData on our disk representation of the HTLC so that
callers do not need to worry about the underlying storage detail.
This commit is contained in:
Carla Kirk-Cohen committed 2024-03-27 09:38:56 -04:00
1 parent 7596e764ac
commit f090a64142
5 files changed
+246 -40

No files matched your search

+71 -13
View File
@@ -31,6 +31,7 @@ import (
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain"
"github.com/lightningnetwork/lnd/tlv"
)
var (
@@ -371,6 +372,12 @@ type PaymentDescriptor struct {
// isForwarded denotes if an incoming HTLC has been forwarded to any
// possible upstream peers in the route.
isForwarded bool
// BlindingPoint is an optional ephemeral key used in route blinding.
// This value is set for nodes that are relaying payments inside of a
// blinded route (ie, not the introduction node) from update_add_htlc's
// TLVs.
BlindingPoint *btcec.PublicKey
}
// PayDescsFromRemoteLogUpdates converts a slice of LogUpdates received from the
@@ -411,6 +418,7 @@ func PayDescsFromRemoteLogUpdates(chanID lnwire.ShortChannelID, height uint64,
Height: height,
Index: uint16(i),
},
BlindingPoint: wireMsg.BlingingPointOrNil(),
}
pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob))
copy(pd.OnionBlob[:], wireMsg.OnionBlob[:])
@@ -736,6 +744,14 @@ func (c *commitment) toDiskCommit(ourCommit bool) *channeldb.ChannelCommitment {
Incoming: false,
}
copy(h.OnionBlob[:], htlc.OnionBlob)
if htlc.BlindingPoint != nil {
h.BlindingPoint = tlv.SomeRecordT(
//nolint:lll
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](
htlc.BlindingPoint,
),
)
}
if ourCommit && htlc.sig != nil {
h.Signature = htlc.sig.Serialize()
@@ -760,7 +776,14 @@ func (c *commitment) toDiskCommit(ourCommit bool) *channeldb.ChannelCommitment {
Incoming: true,
}
copy(h.OnionBlob[:], htlc.OnionBlob)
if htlc.BlindingPoint != nil {
h.BlindingPoint = tlv.SomeRecordT(
//nolint:lll
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](
htlc.BlindingPoint,
),
)
}
if ourCommit && htlc.sig != nil {
h.Signature = htlc.sig.Serialize()
}
@@ -859,6 +882,12 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight,
theirWitnessScript: theirWitnessScript,
}
htlc.BlindingPoint.WhenSome(func(b tlv.RecordT[
lnwire.BlindingPointTlvType, *btcec.PublicKey]) {
pd.BlindingPoint = b.Val
})
return pd, nil
}
@@ -1548,6 +1577,7 @@ func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate,
HtlcIndex: wireMsg.ID,
LogIndex: logUpdate.LogIndex,
addCommitHeightRemote: commitHeight,
BlindingPoint: wireMsg.BlingingPointOrNil(),
}
pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob))
copy(pd.OnionBlob[:], wireMsg.OnionBlob[:])
@@ -1745,6 +1775,7 @@ func (lc *LightningChannel) remoteLogUpdateToPayDesc(logUpdate *channeldb.LogUpd
HtlcIndex: wireMsg.ID,
LogIndex: logUpdate.LogIndex,
addCommitHeightLocal: commitHeight,
BlindingPoint: wireMsg.BlingingPointOrNil(),
}
pd.OnionBlob = make([]byte, len(wireMsg.OnionBlob))
copy(pd.OnionBlob, wireMsg.OnionBlob[:])
@@ -3607,6 +3638,14 @@ func (lc *LightningChannel) createCommitDiff(
PaymentHash: pd.RHash,
}
copy(htlc.OnionBlob[:], pd.OnionBlob)
if pd.BlindingPoint != nil {
htlc.BlindingPoint = tlv.SomeRecordT(
//nolint:lll
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](
pd.BlindingPoint,
),
)
}
logUpdate.UpdateMsg = htlc
// Gather any references for circuits opened by this Add
@@ -3736,12 +3775,21 @@ func (lc *LightningChannel) getUnsignedAckedUpdates() []channeldb.LogUpdate {
// four messages that it corresponds to.
switch pd.EntryType {
case Add:
var b lnwire.BlindingPointRecord
if pd.BlindingPoint != nil {
tlv.SomeRecordT(
//nolint:lll
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](pd.BlindingPoint),
)
}
htlc := &lnwire.UpdateAddHTLC{
ChanID: chanID,
ID: pd.HtlcIndex,
Amount: pd.Amount,
Expiry: pd.Timeout,
PaymentHash: pd.RHash,
ChanID: chanID,
ID: pd.HtlcIndex,
Amount: pd.Amount,
Expiry: pd.Timeout,
PaymentHash: pd.RHash,
BlindingPoint: b,
}
copy(htlc.OnionBlob[:], pd.OnionBlob)
logUpdate.UpdateMsg = htlc
@@ -5742,6 +5790,14 @@ func (lc *LightningChannel) ReceiveRevocation(revMsg *lnwire.RevokeAndAck) (
Expiry: pd.Timeout,
PaymentHash: pd.RHash,
}
if pd.BlindingPoint != nil {
htlc.BlindingPoint = tlv.SomeRecordT(
//nolint:lll
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](
pd.BlindingPoint,
),
)
}
copy(htlc.OnionBlob[:], pd.OnionBlob)
logUpdate.UpdateMsg = htlc
addUpdates = append(addUpdates, logUpdate)
@@ -6079,6 +6135,7 @@ func (lc *LightningChannel) htlcAddDescriptor(htlc *lnwire.UpdateAddHTLC,
HtlcIndex: lc.localUpdateLog.htlcCounter,
OnionBlob: htlc.OnionBlob[:],
OpenCircuitKey: openKey,
BlindingPoint: htlc.BlingingPointOrNil(),
}
}
@@ -6129,13 +6186,14 @@ func (lc *LightningChannel) ReceiveHTLC(htlc *lnwire.UpdateAddHTLC) (uint64, err
}
pd := &PaymentDescriptor{
EntryType: Add,
RHash: PaymentHash(htlc.PaymentHash),
Timeout: htlc.Expiry,
Amount: htlc.Amount,
LogIndex: lc.remoteUpdateLog.logIndex,
HtlcIndex: lc.remoteUpdateLog.htlcCounter,
OnionBlob: htlc.OnionBlob[:],
EntryType: Add,
RHash: PaymentHash(htlc.PaymentHash),
Timeout: htlc.Expiry,
Amount: htlc.Amount,
LogIndex: lc.remoteUpdateLog.logIndex,
HtlcIndex: lc.remoteUpdateLog.htlcCounter,
OnionBlob: htlc.OnionBlob[:],
BlindingPoint: htlc.BlingingPointOrNil(),
}
localACKedIndex := lc.remoteCommitChain.tail().ourMessageIndex
+66 -3
View File
@@ -25,6 +25,7 @@ import (
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/tlv"
"github.com/stretchr/testify/require"
)
@@ -10419,8 +10420,9 @@ func createRandomHTLC(t *testing.T, incoming bool) channeldb.HTLC {
_, err = rand.Read(sig)
require.NoError(t, err)
extra := make([]byte, 1000)
_, err = rand.Read(extra)
blinding, err := pubkeyFromHex(
"0228f2af0abe322403480fb3ee172f7f1601e67d1da6cad40b54c4468d48236c39", //nolint:lll
)
require.NoError(t, err)
return channeldb.HTLC{
@@ -10433,7 +10435,10 @@ func createRandomHTLC(t *testing.T, incoming bool) channeldb.HTLC {
OnionBlob: onionBlob,
HtlcIndex: rand.Uint64(),
LogIndex: rand.Uint64(),
ExtraData: extra,
BlindingPoint: tlv.SomeRecordT(
//nolint:lll
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](blinding),
),
}
}
@@ -11000,3 +11005,61 @@ func TestEnforceFeeBuffer(t *testing.T) {
require.Equal(t, aliceBalance, expectedAmt)
}
// TestBlindingPointPersistence tests persistence of blinding points attached
// to htlcs across restarts.
func TestBlindingPointPersistence(t *testing.T) {
// Create a test channel which will be used for the duration of this
// test. The channel will be funded evenly with Alice having 5 BTC,
// and Bob having 5 BTC.
aliceChannel, bobChannel, err := CreateTestChannels(
t, channeldb.SingleFunderTweaklessBit,
)
require.NoError(t, err, "unable to create test channels")
// Send a HTLC from Alice to Bob that has a blinding point populated.
htlc, _ := createHTLC(0, 100_000_000)
blinding, err := pubkeyFromHex(
"0228f2af0abe322403480fb3ee172f7f1601e67d1da6cad40b54c4468d48236c39", //nolint:lll
)
require.NoError(t, err)
htlc.BlindingPoint = tlv.SomeRecordT(
tlv.NewPrimitiveRecord[lnwire.BlindingPointTlvType](blinding),
)
_, err = aliceChannel.AddHTLC(htlc, nil)
require.NoError(t, err)
_, err = bobChannel.ReceiveHTLC(htlc)
require.NoError(t, err)
// Now, Alice will send a new commitment to Bob, which will persist our
// pending HTLC to disk.
aliceCommit, err := aliceChannel.SignNextCommitment()
require.NoError(t, err, "unable to sign commitment")
// Restart alice to force fetching state from disk.
aliceChannel, err = restartChannel(aliceChannel)
require.NoError(t, err, "unable to restart alice")
// Assert that the blinding point is restored from disk.
remoteCommit := aliceChannel.remoteCommitChain.tip()
require.Len(t, remoteCommit.outgoingHTLCs, 1)
require.Equal(t, blinding, remoteCommit.outgoingHTLCs[0].BlindingPoint)
// Next, update bob's commitment and assert that we can still retrieve
// his incoming blinding point after restart.
err = bobChannel.ReceiveNewCommitment(aliceCommit.CommitSigs)
require.NoError(t, err, "bob unable to receive new commitment")
_, _, _, err = bobChannel.RevokeCurrentCommitment()
require.NoError(t, err, "bob unable to revoke current commitment")
bobChannel, err = restartChannel(bobChannel)
require.NoError(t, err, "unable to restart bob's channel")
// Assert that Bob is able to recover the blinding point from disk.
bobCommit := bobChannel.localCommitChain.tip()
require.Len(t, bobCommit.incomingHTLCs, 1)
require.Equal(t, blinding, bobCommit.incomingHTLCs[0].BlindingPoint)
}