From 2fc8e9d617270bb7fde31951367d1ad8553272c7 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Fri, 19 May 2023 10:58:05 -0400 Subject: [PATCH] multi: update channel db HTLC OnionBlob to array We know that onion blobs in lightning are _exactly_ 1366 bytes in lightning, but they are currently expressed as a byte slice in channeldb's HTLC struct. Blobs are currently serialized as var bytes, so we can take advantage of this known length and variable length to add additional data to the inline serialization of our HTLCs, which are otherwise not easily extensible (without creating a new bucket). --- channeldb/channel.go | 11 +++++++---- channeldb/channel_test.go | 14 +++++++++----- contractcourt/briefcase_test.go | 3 ++- contractcourt/htlc_incoming_contest_resolver.go | 2 +- contractcourt/htlc_incoming_resolver_test.go | 7 ++++--- .../htlc_outgoing_contest_resolver_test.go | 3 ++- contractcourt/htlc_success_resolver_test.go | 3 ++- lnmock/routing.go | 16 ++++++++++++++++ lnwallet/channel.go | 4 +--- 9 files changed, 44 insertions(+), 19 deletions(-) create mode 100644 lnmock/routing.go diff --git a/channeldb/channel.go b/channeldb/channel.go index dc0bf0058..f98632196 100644 --- a/channeldb/channel.go +++ b/channeldb/channel.go @@ -2001,7 +2001,7 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC { // which ones are present on their commitment. remoteHtlcs := make(map[[32]byte]struct{}) for _, htlc := range c.RemoteCommitment.Htlcs { - onionHash := sha256.Sum256(htlc.OnionBlob) + onionHash := sha256.Sum256(htlc.OnionBlob[:]) remoteHtlcs[onionHash] = struct{}{} } @@ -2009,7 +2009,7 @@ func (c *OpenChannel) ActiveHtlcs() []HTLC { // as active if *we* know them as well. activeHtlcs := make([]HTLC, 0, len(remoteHtlcs)) for _, htlc := range c.LocalCommitment.Htlcs { - onionHash := sha256.Sum256(htlc.OnionBlob) + onionHash := sha256.Sum256(htlc.OnionBlob[:]) if _, ok := remoteHtlcs[onionHash]; !ok { continue } @@ -2056,7 +2056,7 @@ type HTLC struct { // OnionBlob is an opaque blob which is used to complete multi-hop // routing. - OnionBlob []byte + OnionBlob [lnwire.OnionPacketSize]byte // HtlcIndex is the HTLC counter index of this active, outstanding // HTLC. This differs from the LogIndex, as the HtlcIndex is only @@ -2113,14 +2113,17 @@ func DeserializeHtlcs(r io.Reader) ([]HTLC, error) { htlcs = make([]HTLC, numHtlcs) for i := uint16(0); i < numHtlcs; i++ { + var onionBlob []byte if err := ReadElements(r, &htlcs[i].Signature, &htlcs[i].RHash, &htlcs[i].Amt, &htlcs[i].RefundTimeout, &htlcs[i].OutputIndex, - &htlcs[i].Incoming, &htlcs[i].OnionBlob, + &htlcs[i].Incoming, &onionBlob, &htlcs[i].HtlcIndex, &htlcs[i].LogIndex, ); err != nil { return htlcs, err } + + copy(htlcs[i].OnionBlob[:], onionBlob) } return htlcs, nil diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index ccb6b10a8..7896bcaf4 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -19,6 +19,7 @@ import ( "github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/kvdb" + "github.com/lightningnetwork/lnd/lnmock" "github.com/lightningnetwork/lnd/lntest/channels" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" @@ -366,12 +367,13 @@ func TestOpenChannelPutGetDelete(t *testing.T) { // Create the test channel state, with additional htlcs on the local // and remote commitment. localHtlcs := []HTLC{ - {Signature: testSig.Serialize(), + { + Signature: testSig.Serialize(), Incoming: true, Amt: 10, RHash: key, RefundTimeout: 1, - OnionBlob: []byte("onionblob"), + OnionBlob: lnmock.MockOnion(), }, } @@ -382,7 +384,7 @@ func TestOpenChannelPutGetDelete(t *testing.T) { Amt: 10, RHash: key, RefundTimeout: 1, - OnionBlob: []byte("onionblob"), + OnionBlob: lnmock.MockOnion(), }, } @@ -612,8 +614,10 @@ func TestChannelStateTransition(t *testing.T) { LogIndex: uint64(i * 2), HtlcIndex: uint64(i), } - htlc.OnionBlob = make([]byte, 10) - copy(htlc.OnionBlob[:], bytes.Repeat([]byte{2}, 10)) + copy( + htlc.OnionBlob[:], + bytes.Repeat([]byte{2}, lnwire.OnionPacketSize), + ) htlcs = append(htlcs, htlc) htlcAmt += htlc.Amt } diff --git a/contractcourt/briefcase_test.go b/contractcourt/briefcase_test.go index 8bc0209f1..140a85c22 100644 --- a/contractcourt/briefcase_test.go +++ b/contractcourt/briefcase_test.go @@ -16,6 +16,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/kvdb" + "github.com/lightningnetwork/lnd/lnmock" "github.com/lightningnetwork/lnd/lntest/channels" "github.com/lightningnetwork/lnd/lnwallet" "github.com/stretchr/testify/require" @@ -745,7 +746,7 @@ func TestCommitSetStorage(t *testing.T) { activeHTLCs := []channeldb.HTLC{ { Amt: 1000, - OnionBlob: make([]byte, 0), + OnionBlob: lnmock.MockOnion(), Signature: make([]byte, 0), }, } diff --git a/contractcourt/htlc_incoming_contest_resolver.go b/contractcourt/htlc_incoming_contest_resolver.go index 79f29e494..f3df57b10 100644 --- a/contractcourt/htlc_incoming_contest_resolver.go +++ b/contractcourt/htlc_incoming_contest_resolver.go @@ -487,7 +487,7 @@ func (h *htlcIncomingContestResolver) Supplement(htlc channeldb.HTLC) { func (h *htlcIncomingContestResolver) decodePayload() (*hop.Payload, []byte, error) { - onionReader := bytes.NewReader(h.htlc.OnionBlob) + onionReader := bytes.NewReader(h.htlc.OnionBlob[:]) iterator, err := h.OnionProcessor.ReconstructHopIterator( onionReader, h.htlc.RHash[:], ) diff --git a/contractcourt/htlc_incoming_resolver_test.go b/contractcourt/htlc_incoming_resolver_test.go index 620acdc20..e8532abe5 100644 --- a/contractcourt/htlc_incoming_resolver_test.go +++ b/contractcourt/htlc_incoming_resolver_test.go @@ -13,6 +13,7 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/invoices" "github.com/lightningnetwork/lnd/kvdb" + "github.com/lightningnetwork/lnd/lnmock" "github.com/lightningnetwork/lnd/lntest/mock" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" @@ -29,7 +30,6 @@ var ( testResPreimage = lntypes.Preimage{1, 2, 3} testResHash = testResPreimage.Hash() testResCircuitKey = models.CircuitKey{} - testOnionBlob = []byte{4, 5, 6} testAcceptHeight int32 = 1234 testHtlcAmount = 2300 ) @@ -139,8 +139,9 @@ func TestHtlcIncomingResolverExitSettle(t *testing.T) { ctx.waitForResult(true) + expetedOnion := lnmock.MockOnion() if !bytes.Equal( - ctx.onionProcessor.offeredOnionBlob, testOnionBlob, + ctx.onionProcessor.offeredOnionBlob, expetedOnion[:], ) { t.Fatal("unexpected onion blob") @@ -375,7 +376,7 @@ func newIncomingResolverTestContext(t *testing.T, isExit bool) *incomingResolver htlc: channeldb.HTLC{ Amt: lnwire.MilliSatoshi(testHtlcAmount), RHash: testResHash, - OnionBlob: testOnionBlob, + OnionBlob: lnmock.MockOnion(), }, }, htlcExpiry: testHtlcExpiry, diff --git a/contractcourt/htlc_outgoing_contest_resolver_test.go b/contractcourt/htlc_outgoing_contest_resolver_test.go index 751f8b971..f83d17749 100644 --- a/contractcourt/htlc_outgoing_contest_resolver_test.go +++ b/contractcourt/htlc_outgoing_contest_resolver_test.go @@ -9,6 +9,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/kvdb" + "github.com/lightningnetwork/lnd/lnmock" "github.com/lightningnetwork/lnd/lntest/mock" "github.com/lightningnetwork/lnd/lntypes" "github.com/lightningnetwork/lnd/lnwallet" @@ -183,7 +184,7 @@ func newOutgoingResolverTestContext(t *testing.T) *outgoingResolverTestContext { htlc: channeldb.HTLC{ Amt: lnwire.MilliSatoshi(testHtlcAmount), RHash: testResHash, - OnionBlob: testOnionBlob, + OnionBlob: lnmock.MockOnion(), }, }, } diff --git a/contractcourt/htlc_success_resolver_test.go b/contractcourt/htlc_success_resolver_test.go index 1303291d0..d2c9bf051 100644 --- a/contractcourt/htlc_success_resolver_test.go +++ b/contractcourt/htlc_success_resolver_test.go @@ -14,6 +14,7 @@ import ( "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/kvdb" + "github.com/lightningnetwork/lnd/lnmock" "github.com/lightningnetwork/lnd/lntest/mock" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" @@ -110,7 +111,7 @@ func newHtlcResolverTestContext(t *testing.T, htlc := channeldb.HTLC{ RHash: testResHash, - OnionBlob: testOnionBlob, + OnionBlob: lnmock.MockOnion(), Amt: testHtlcAmt, } diff --git a/lnmock/routing.go b/lnmock/routing.go new file mode 100644 index 000000000..260cce6de --- /dev/null +++ b/lnmock/routing.go @@ -0,0 +1,16 @@ +package lnmock + +import ( + "bytes" + + "github.com/lightningnetwork/lnd/lnwire" +) + +// MockOnion returns a mock onion payload. +func MockOnion() [lnwire.OnionPacketSize]byte { + var onion [lnwire.OnionPacketSize]byte + onionBlob := bytes.Repeat([]byte{1}, lnwire.OnionPacketSize) + copy(onion[:], onionBlob) + + return onion +} diff --git a/lnwallet/channel.go b/lnwallet/channel.go index d715d5ed6..ad08557a7 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -745,7 +745,6 @@ func (c *commitment) toDiskCommit(ourCommit bool) *channeldb.ChannelCommitment { LogIndex: htlc.LogIndex, Incoming: false, } - h.OnionBlob = make([]byte, len(htlc.OnionBlob)) copy(h.OnionBlob[:], htlc.OnionBlob) if ourCommit && htlc.sig != nil { @@ -770,7 +769,6 @@ func (c *commitment) toDiskCommit(ourCommit bool) *channeldb.ChannelCommitment { LogIndex: htlc.LogIndex, Incoming: true, } - h.OnionBlob = make([]byte, len(htlc.OnionBlob)) copy(h.OnionBlob[:], htlc.OnionBlob) if ourCommit && htlc.sig != nil { @@ -859,7 +857,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate chainfee.SatPerKWeight, EntryType: Add, HtlcIndex: htlc.HtlcIndex, LogIndex: htlc.LogIndex, - OnionBlob: htlc.OnionBlob, + OnionBlob: htlc.OnionBlob[:], localOutputIndex: localOutputIndex, remoteOutputIndex: remoteOutputIndex, ourPkScript: ourP2WSH,