channeldb+routing: cache circuit and onion blob for htlc attempt

This commit caches the creation of sphinx circuit and onion blob to
avoid re-creating them again.
This commit is contained in:
yyforyongyu committed 2025-02-10 11:28:23 +08:00
1 parent 46eb811543
commit f96eb50ca8
8 files changed
+187 -121

No files matched your search

+11 -3
View File
@@ -535,15 +535,23 @@ func genInfo() (*channeldb.PaymentCreationInfo, *channeldb.HTLCAttemptInfo,
}
rhash := sha256.Sum256(preimage[:])
var hash lntypes.Hash
copy(hash[:], rhash[:])
attempt, err := channeldb.NewHtlcAttempt(
1, priv, testRoute, time.Time{}, &hash,
)
if err != nil {
return nil, nil, lntypes.Preimage{}, err
}
return &channeldb.PaymentCreationInfo{
PaymentIdentifier: rhash,
Value: testRoute.ReceiverAmt(),
CreationTime: time.Unix(time.Now().Unix(), 0),
PaymentRequest: []byte("hola"),
},
&channeldb.NewHtlcAttempt(
1, priv, testRoute, time.Time{}, nil,
).HTLCAttemptInfo, preimage, nil
&attempt.HTLCAttemptInfo, preimage, nil
}
func genPreimage() ([32]byte, error) {
+5 -10
View File
@@ -489,9 +489,8 @@ func (p *paymentLifecycle) collectResult(attempt *channeldb.HTLCAttempt) (
log.Tracef("Collecting result for attempt %v", spew.Sdump(attempt))
// Regenerate the circuit for this attempt.
_, circuit, err := generateSphinxPacket(
&attempt.Route, attempt.Hash[:], attempt.SessionKey(),
)
circuit, err := attempt.Circuit()
// TODO(yy): We generate this circuit to create the error decryptor,
// which is then used in htlcswitch as the deobfuscator to decode the
// error from `UpdateFailHTLC`. However, suppose it's an
@@ -667,11 +666,9 @@ func (p *paymentLifecycle) createNewPaymentAttempt(rt *route.Route,
// We now have all the information needed to populate the current
// attempt information.
attempt := channeldb.NewHtlcAttempt(
return channeldb.NewHtlcAttempt(
attemptID, sessionKey, *rt, p.router.cfg.Clock.Now(), &hash,
)
return attempt, nil
}
// sendAttempt attempts to send the current attempt to the switch to complete
@@ -703,9 +700,7 @@ func (p *paymentLifecycle) sendAttempt(
// Generate the raw encoded sphinx packet to be included along
// with the htlcAdd message that we send directly to the
// switch.
onionBlob, _, err := generateSphinxPacket(
&rt, attempt.Hash[:], attempt.SessionKey(),
)
onionBlob, err := attempt.OnionBlob()
if err != nil {
log.Errorf("Failed to create onion blob: attempt=%d in "+
"payment=%v, err:%v", attempt.AttemptID,
@@ -714,7 +709,7 @@ func (p *paymentLifecycle) sendAttempt(
return p.failAttempt(attempt.AttemptID, err)
}
copy(htlcAdd.OnionBlob[:], onionBlob)
htlcAdd.OnionBlob = onionBlob
// Send it to the Switch. When this method returns we assume
// the Switch successfully has persisted the payment attempt,
-67
View File
@@ -1,7 +1,6 @@
package routing
import (
"bytes"
"context"
"fmt"
"math"
@@ -15,7 +14,6 @@ import (
"github.com/btcsuite/btcd/btcutil"
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/amp"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/clock"
@@ -722,71 +720,6 @@ func generateNewSessionKey() (*btcec.PrivateKey, error) {
return btcec.NewPrivateKey()
}
// generateSphinxPacket generates then encodes a sphinx packet which encodes
// the onion route specified by the passed layer 3 route. The blob returned
// from this function can immediately be included within an HTLC add packet to
// be sent to the first hop within the route.
func generateSphinxPacket(rt *route.Route, paymentHash []byte,
sessionKey *btcec.PrivateKey) ([]byte, *sphinx.Circuit, error) {
// Now that we know we have an actual route, we'll map the route into a
// sphinx payment path which includes per-hop payloads for each hop
// that give each node within the route the necessary information
// (fees, CLTV value, etc.) to properly forward the payment.
sphinxPath, err := rt.ToSphinxPath()
if err != nil {
return nil, nil, err
}
log.Tracef("Constructed per-hop payloads for payment_hash=%x: %v",
paymentHash, lnutils.NewLogClosure(func() string {
path := make(
[]sphinx.OnionHop, sphinxPath.TrueRouteLength(),
)
for i := range path {
hopCopy := sphinxPath[i]
path[i] = hopCopy
}
return spew.Sdump(path)
}),
)
// Next generate the onion routing packet which allows us to perform
// privacy preserving source routing across the network.
sphinxPacket, err := sphinx.NewOnionPacket(
sphinxPath, sessionKey, paymentHash,
sphinx.DeterministicPacketFiller,
)
if err != nil {
return nil, nil, err
}
// Finally, encode Sphinx packet using its wire representation to be
// included within the HTLC add packet.
var onionBlob bytes.Buffer
if err := sphinxPacket.Encode(&onionBlob); err != nil {
return nil, nil, err
}
log.Tracef("Generated sphinx packet: %v",
lnutils.NewLogClosure(func() string {
// We make a copy of the ephemeral key and unset the
// internal curve here in order to keep the logs from
// getting noisy.
key := *sphinxPacket.EphemeralKey
packetCopy := *sphinxPacket
packetCopy.EphemeralKey = &key
return spew.Sdump(packetCopy)
}),
)
return onionBlob.Bytes(), &sphinx.Circuit{
SessionKey: sessionKey,
PaymentPath: sphinxPath.NodeKeys(),
}, nil
}
// LightningPayment describes a payment to be sent through the network to the
// final destination.
type LightningPayment struct {
-19
View File
@@ -48,13 +48,6 @@ var (
testFeatures = lnwire.NewFeatureVector(nil, lnwire.Features)
testHash = [32]byte{
0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9,
0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
}
testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
priv1, _ = btcec.NewPrivateKey()
@@ -1235,18 +1228,6 @@ func TestFindPathFeeWeighting(t *testing.T) {
require.Equal(t, ctx.aliases["luoji"], path[0].policy.ToNodePubKey())
}
// TestEmptyRoutesGenerateSphinxPacket tests that the generateSphinxPacket
// function is able to gracefully handle being passed a nil set of hops for the
// route by the caller.
func TestEmptyRoutesGenerateSphinxPacket(t *testing.T) {
t.Parallel()
sessionKey, _ := btcec.NewPrivateKey()
emptyRoute := &route.Route{}
_, _, err := generateSphinxPacket(emptyRoute, testHash[:], sessionKey)
require.ErrorIs(t, err, route.ErrNoRouteHopsProvided)
}
// TestUnknownErrorSource tests that if the source of an error is unknown, all
// edges along the route will be pruned.
func TestUnknownErrorSource(t *testing.T) {