mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-15 03:12:26 +02:00
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:
parent
46eb811543
commit
f96eb50ca8
@ -10,7 +10,10 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
sphinx "github.com/lightningnetwork/lightning-onion"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
"github.com/lightningnetwork/lnd/lnutils"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
)
|
)
|
||||||
@ -45,12 +48,19 @@ type HTLCAttemptInfo struct {
|
|||||||
// in which the payment's PaymentHash in the PaymentCreationInfo should
|
// in which the payment's PaymentHash in the PaymentCreationInfo should
|
||||||
// be used.
|
// be used.
|
||||||
Hash *lntypes.Hash
|
Hash *lntypes.Hash
|
||||||
|
|
||||||
|
// onionBlob is the cached value for onion blob created from the sphinx
|
||||||
|
// construction.
|
||||||
|
onionBlob [lnwire.OnionPacketSize]byte
|
||||||
|
|
||||||
|
// circuit is the cached value for sphinx circuit.
|
||||||
|
circuit *sphinx.Circuit
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHtlcAttempt creates a htlc attempt.
|
// NewHtlcAttempt creates a htlc attempt.
|
||||||
func NewHtlcAttempt(attemptID uint64, sessionKey *btcec.PrivateKey,
|
func NewHtlcAttempt(attemptID uint64, sessionKey *btcec.PrivateKey,
|
||||||
route route.Route, attemptTime time.Time,
|
route route.Route, attemptTime time.Time,
|
||||||
hash *lntypes.Hash) *HTLCAttempt {
|
hash *lntypes.Hash) (*HTLCAttempt, error) {
|
||||||
|
|
||||||
var scratch [btcec.PrivKeyBytesLen]byte
|
var scratch [btcec.PrivKeyBytesLen]byte
|
||||||
copy(scratch[:], sessionKey.Serialize())
|
copy(scratch[:], sessionKey.Serialize())
|
||||||
@ -64,7 +74,11 @@ func NewHtlcAttempt(attemptID uint64, sessionKey *btcec.PrivateKey,
|
|||||||
Hash: hash,
|
Hash: hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &HTLCAttempt{HTLCAttemptInfo: info}
|
if err := info.attachOnionBlobAndCircuit(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &HTLCAttempt{HTLCAttemptInfo: info}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionKey returns the ephemeral key used for a htlc attempt. This function
|
// SessionKey returns the ephemeral key used for a htlc attempt. This function
|
||||||
@ -79,6 +93,45 @@ func (h *HTLCAttemptInfo) SessionKey() *btcec.PrivateKey {
|
|||||||
return h.cachedSessionKey
|
return h.cachedSessionKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnionBlob returns the onion blob created from the sphinx construction.
|
||||||
|
func (h *HTLCAttemptInfo) OnionBlob() ([lnwire.OnionPacketSize]byte, error) {
|
||||||
|
var zeroBytes [lnwire.OnionPacketSize]byte
|
||||||
|
if h.onionBlob == zeroBytes {
|
||||||
|
if err := h.attachOnionBlobAndCircuit(); err != nil {
|
||||||
|
return zeroBytes, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.onionBlob, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Circuit returns the sphinx circuit for this attempt.
|
||||||
|
func (h *HTLCAttemptInfo) Circuit() (*sphinx.Circuit, error) {
|
||||||
|
if h.circuit == nil {
|
||||||
|
if err := h.attachOnionBlobAndCircuit(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.circuit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// attachOnionBlobAndCircuit creates a sphinx packet and caches the onion blob
|
||||||
|
// and circuit for this attempt.
|
||||||
|
func (h *HTLCAttemptInfo) attachOnionBlobAndCircuit() error {
|
||||||
|
onionBlob, circuit, err := generateSphinxPacket(
|
||||||
|
&h.Route, h.Hash[:], h.SessionKey(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(h.onionBlob[:], onionBlob)
|
||||||
|
h.circuit = circuit
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// HTLCAttempt contains information about a specific HTLC attempt for a given
|
// HTLCAttempt contains information about a specific HTLC attempt for a given
|
||||||
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
|
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
|
||||||
// as a timestamp and any known outcome of the attempt.
|
// as a timestamp and any known outcome of the attempt.
|
||||||
@ -629,3 +682,69 @@ func serializeTime(w io.Writer, t time.Time) error {
|
|||||||
_, err := w.Write(scratch[:])
|
_, err := w.Write(scratch[:])
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
@ -5,12 +5,22 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/lightningnetwork/lnd/lntypes"
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing/route"
|
"github.com/lightningnetwork/lnd/routing/route"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// TestLazySessionKeyDeserialize tests that we can read htlc attempt session
|
// TestLazySessionKeyDeserialize tests that we can read htlc attempt session
|
||||||
// keys that were previously serialized as a private key as raw bytes.
|
// keys that were previously serialized as a private key as raw bytes.
|
||||||
func TestLazySessionKeyDeserialize(t *testing.T) {
|
func TestLazySessionKeyDeserialize(t *testing.T) {
|
||||||
@ -578,3 +588,15 @@ func makeAttemptInfo(total, amtForwarded int) HTLCAttemptInfo {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
@ -28,7 +28,7 @@ func genPreimage() ([32]byte, error) {
|
|||||||
return preimage, nil
|
return preimage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func genInfo() (*PaymentCreationInfo, *HTLCAttemptInfo,
|
func genInfo(t *testing.T) (*PaymentCreationInfo, *HTLCAttemptInfo,
|
||||||
lntypes.Preimage, error) {
|
lntypes.Preimage, error) {
|
||||||
|
|
||||||
preimage, err := genPreimage()
|
preimage, err := genPreimage()
|
||||||
@ -38,9 +38,14 @@ func genInfo() (*PaymentCreationInfo, *HTLCAttemptInfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rhash := sha256.Sum256(preimage[:])
|
rhash := sha256.Sum256(preimage[:])
|
||||||
attempt := NewHtlcAttempt(
|
var hash lntypes.Hash
|
||||||
0, priv, *testRoute.Copy(), time.Time{}, nil,
|
copy(hash[:], rhash[:])
|
||||||
|
|
||||||
|
attempt, err := NewHtlcAttempt(
|
||||||
|
0, priv, *testRoute.Copy(), time.Time{}, &hash,
|
||||||
)
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
return &PaymentCreationInfo{
|
return &PaymentCreationInfo{
|
||||||
PaymentIdentifier: rhash,
|
PaymentIdentifier: rhash,
|
||||||
Value: testRoute.ReceiverAmt(),
|
Value: testRoute.ReceiverAmt(),
|
||||||
@ -60,7 +65,7 @@ func TestPaymentControlSwitchFail(t *testing.T) {
|
|||||||
|
|
||||||
pControl := NewPaymentControl(db)
|
pControl := NewPaymentControl(db)
|
||||||
|
|
||||||
info, attempt, preimg, err := genInfo()
|
info, attempt, preimg, err := genInfo(t)
|
||||||
require.NoError(t, err, "unable to generate htlc message")
|
require.NoError(t, err, "unable to generate htlc message")
|
||||||
|
|
||||||
// Sends base htlc message which initiate StatusInFlight.
|
// Sends base htlc message which initiate StatusInFlight.
|
||||||
@ -196,7 +201,7 @@ func TestPaymentControlSwitchDoubleSend(t *testing.T) {
|
|||||||
|
|
||||||
pControl := NewPaymentControl(db)
|
pControl := NewPaymentControl(db)
|
||||||
|
|
||||||
info, attempt, preimg, err := genInfo()
|
info, attempt, preimg, err := genInfo(t)
|
||||||
require.NoError(t, err, "unable to generate htlc message")
|
require.NoError(t, err, "unable to generate htlc message")
|
||||||
|
|
||||||
// Sends base htlc message which initiate base status and move it to
|
// Sends base htlc message which initiate base status and move it to
|
||||||
@ -266,7 +271,7 @@ func TestPaymentControlSuccessesWithoutInFlight(t *testing.T) {
|
|||||||
|
|
||||||
pControl := NewPaymentControl(db)
|
pControl := NewPaymentControl(db)
|
||||||
|
|
||||||
info, _, preimg, err := genInfo()
|
info, _, preimg, err := genInfo(t)
|
||||||
require.NoError(t, err, "unable to generate htlc message")
|
require.NoError(t, err, "unable to generate htlc message")
|
||||||
|
|
||||||
// Attempt to complete the payment should fail.
|
// Attempt to complete the payment should fail.
|
||||||
@ -291,7 +296,7 @@ func TestPaymentControlFailsWithoutInFlight(t *testing.T) {
|
|||||||
|
|
||||||
pControl := NewPaymentControl(db)
|
pControl := NewPaymentControl(db)
|
||||||
|
|
||||||
info, _, _, err := genInfo()
|
info, _, _, err := genInfo(t)
|
||||||
require.NoError(t, err, "unable to generate htlc message")
|
require.NoError(t, err, "unable to generate htlc message")
|
||||||
|
|
||||||
// Calling Fail should return an error.
|
// Calling Fail should return an error.
|
||||||
@ -346,7 +351,7 @@ func TestPaymentControlDeleteNonInFlight(t *testing.T) {
|
|||||||
var numSuccess, numInflight int
|
var numSuccess, numInflight int
|
||||||
|
|
||||||
for _, p := range payments {
|
for _, p := range payments {
|
||||||
info, attempt, preimg, err := genInfo()
|
info, attempt, preimg, err := genInfo(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to generate htlc message: %v", err)
|
t.Fatalf("unable to generate htlc message: %v", err)
|
||||||
}
|
}
|
||||||
@ -684,7 +689,7 @@ func TestPaymentControlMultiShard(t *testing.T) {
|
|||||||
|
|
||||||
pControl := NewPaymentControl(db)
|
pControl := NewPaymentControl(db)
|
||||||
|
|
||||||
info, attempt, preimg, err := genInfo()
|
info, attempt, preimg, err := genInfo(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to generate htlc message: %v", err)
|
t.Fatalf("unable to generate htlc message: %v", err)
|
||||||
}
|
}
|
||||||
@ -948,7 +953,7 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) {
|
|||||||
|
|
||||||
pControl := NewPaymentControl(db)
|
pControl := NewPaymentControl(db)
|
||||||
|
|
||||||
info, attempt, _, err := genInfo()
|
info, attempt, _, err := genInfo(t)
|
||||||
require.NoError(t, err, "unable to generate htlc message")
|
require.NoError(t, err, "unable to generate htlc message")
|
||||||
|
|
||||||
// Init the payment.
|
// Init the payment.
|
||||||
@ -997,7 +1002,7 @@ func TestPaymentControlMPPRecordValidation(t *testing.T) {
|
|||||||
|
|
||||||
// Create and init a new payment. This time we'll check that we cannot
|
// Create and init a new payment. This time we'll check that we cannot
|
||||||
// register an MPP attempt if we already registered a non-MPP one.
|
// register an MPP attempt if we already registered a non-MPP one.
|
||||||
info, attempt, _, err = genInfo()
|
info, attempt, _, err = genInfo(t)
|
||||||
require.NoError(t, err, "unable to generate htlc message")
|
require.NoError(t, err, "unable to generate htlc message")
|
||||||
|
|
||||||
err = pControl.InitPayment(info.PaymentIdentifier, info)
|
err = pControl.InitPayment(info.PaymentIdentifier, info)
|
||||||
@ -1271,7 +1276,7 @@ func createTestPayments(t *testing.T, p *PaymentControl, payments []*payment) {
|
|||||||
attemptID := uint64(0)
|
attemptID := uint64(0)
|
||||||
|
|
||||||
for i := 0; i < len(payments); i++ {
|
for i := 0; i < len(payments); i++ {
|
||||||
info, attempt, preimg, err := genInfo()
|
info, attempt, preimg, err := genInfo(t)
|
||||||
require.NoError(t, err, "unable to generate htlc message")
|
require.NoError(t, err, "unable to generate htlc message")
|
||||||
|
|
||||||
// Set the payment id accordingly in the payments slice.
|
// Set the payment id accordingly in the payments slice.
|
||||||
|
@ -64,7 +64,6 @@ var (
|
|||||||
TotalAmount: 1234567,
|
TotalAmount: 1234567,
|
||||||
SourcePubKey: vertex,
|
SourcePubKey: vertex,
|
||||||
Hops: []*route.Hop{
|
Hops: []*route.Hop{
|
||||||
testHop3,
|
|
||||||
testHop2,
|
testHop2,
|
||||||
testHop1,
|
testHop1,
|
||||||
},
|
},
|
||||||
@ -98,7 +97,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) {
|
func makeFakeInfo(t *testing.T) (*PaymentCreationInfo, *HTLCAttemptInfo) {
|
||||||
var preimg lntypes.Preimage
|
var preimg lntypes.Preimage
|
||||||
copy(preimg[:], rev[:])
|
copy(preimg[:], rev[:])
|
||||||
|
|
||||||
@ -113,9 +112,10 @@ func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) {
|
|||||||
PaymentRequest: []byte("test"),
|
PaymentRequest: []byte("test"),
|
||||||
}
|
}
|
||||||
|
|
||||||
a := NewHtlcAttempt(
|
a, err := NewHtlcAttempt(
|
||||||
44, priv, testRoute, time.Unix(100, 0), &hash,
|
44, priv, testRoute, time.Unix(100, 0), &hash,
|
||||||
)
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
return c, &a.HTLCAttemptInfo
|
return c, &a.HTLCAttemptInfo
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) {
|
|||||||
func TestSentPaymentSerialization(t *testing.T) {
|
func TestSentPaymentSerialization(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
c, s := makeFakeInfo()
|
c, s := makeFakeInfo(t)
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
require.NoError(t, serializePaymentCreationInfo(&b, c), "serialize")
|
require.NoError(t, serializePaymentCreationInfo(&b, c), "serialize")
|
||||||
@ -174,6 +174,9 @@ func TestSentPaymentSerialization(t *testing.T) {
|
|||||||
require.NoError(t, err, "deserialize")
|
require.NoError(t, err, "deserialize")
|
||||||
require.Equal(t, s.Route, newWireInfo.Route)
|
require.Equal(t, s.Route, newWireInfo.Route)
|
||||||
|
|
||||||
|
err = newWireInfo.attachOnionBlobAndCircuit()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Clear routes to allow DeepEqual to compare the remaining fields.
|
// Clear routes to allow DeepEqual to compare the remaining fields.
|
||||||
newWireInfo.Route = route.Route{}
|
newWireInfo.Route = route.Route{}
|
||||||
s.Route = route.Route{}
|
s.Route = route.Route{}
|
||||||
@ -517,7 +520,7 @@ func TestQueryPayments(t *testing.T) {
|
|||||||
|
|
||||||
for i := 0; i < nonDuplicatePayments; i++ {
|
for i := 0; i < nonDuplicatePayments; i++ {
|
||||||
// Generate a test payment.
|
// Generate a test payment.
|
||||||
info, _, preimg, err := genInfo()
|
info, _, preimg, err := genInfo(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unable to create test "+
|
t.Fatalf("unable to create test "+
|
||||||
"payment: %v", err)
|
"payment: %v", err)
|
||||||
@ -618,7 +621,7 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) {
|
|||||||
pControl := NewPaymentControl(db)
|
pControl := NewPaymentControl(db)
|
||||||
|
|
||||||
// Generate a test payment which does not have duplicates.
|
// Generate a test payment which does not have duplicates.
|
||||||
noDuplicates, _, _, err := genInfo()
|
noDuplicates, _, _, err := genInfo(t)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Create a new payment entry in the database.
|
// Create a new payment entry in the database.
|
||||||
@ -632,7 +635,7 @@ func TestFetchPaymentWithSequenceNumber(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Generate a test payment which we will add duplicates to.
|
// Generate a test payment which we will add duplicates to.
|
||||||
hasDuplicates, _, preimg, err := genInfo()
|
hasDuplicates, _, preimg, err := genInfo(t)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Create a new payment entry in the database.
|
// Create a new payment entry in the database.
|
||||||
@ -783,7 +786,7 @@ func putDuplicatePayment(t *testing.T, duplicateBucket kvdb.RwBucket,
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Generate fake information for the duplicate payment.
|
// Generate fake information for the duplicate payment.
|
||||||
info, _, _, err := genInfo()
|
info, _, _, err := genInfo(t)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Write the payment info to disk under the creation info key. This code
|
// Write the payment info to disk under the creation info key. This code
|
||||||
|
@ -535,15 +535,23 @@ func genInfo() (*channeldb.PaymentCreationInfo, *channeldb.HTLCAttemptInfo,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rhash := sha256.Sum256(preimage[:])
|
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{
|
return &channeldb.PaymentCreationInfo{
|
||||||
PaymentIdentifier: rhash,
|
PaymentIdentifier: rhash,
|
||||||
Value: testRoute.ReceiverAmt(),
|
Value: testRoute.ReceiverAmt(),
|
||||||
CreationTime: time.Unix(time.Now().Unix(), 0),
|
CreationTime: time.Unix(time.Now().Unix(), 0),
|
||||||
PaymentRequest: []byte("hola"),
|
PaymentRequest: []byte("hola"),
|
||||||
},
|
},
|
||||||
&channeldb.NewHtlcAttempt(
|
&attempt.HTLCAttemptInfo, preimage, nil
|
||||||
1, priv, testRoute, time.Time{}, nil,
|
|
||||||
).HTLCAttemptInfo, preimage, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func genPreimage() ([32]byte, error) {
|
func genPreimage() ([32]byte, error) {
|
||||||
|
@ -489,9 +489,8 @@ func (p *paymentLifecycle) collectResult(attempt *channeldb.HTLCAttempt) (
|
|||||||
log.Tracef("Collecting result for attempt %v", spew.Sdump(attempt))
|
log.Tracef("Collecting result for attempt %v", spew.Sdump(attempt))
|
||||||
|
|
||||||
// Regenerate the circuit for this attempt.
|
// Regenerate the circuit for this attempt.
|
||||||
_, circuit, err := generateSphinxPacket(
|
circuit, err := attempt.Circuit()
|
||||||
&attempt.Route, attempt.Hash[:], attempt.SessionKey(),
|
|
||||||
)
|
|
||||||
// TODO(yy): We generate this circuit to create the error decryptor,
|
// TODO(yy): We generate this circuit to create the error decryptor,
|
||||||
// which is then used in htlcswitch as the deobfuscator to decode the
|
// which is then used in htlcswitch as the deobfuscator to decode the
|
||||||
// error from `UpdateFailHTLC`. However, suppose it's an
|
// 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
|
// We now have all the information needed to populate the current
|
||||||
// attempt information.
|
// attempt information.
|
||||||
attempt := channeldb.NewHtlcAttempt(
|
return channeldb.NewHtlcAttempt(
|
||||||
attemptID, sessionKey, *rt, p.router.cfg.Clock.Now(), &hash,
|
attemptID, sessionKey, *rt, p.router.cfg.Clock.Now(), &hash,
|
||||||
)
|
)
|
||||||
|
|
||||||
return attempt, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendAttempt attempts to send the current attempt to the switch to complete
|
// 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
|
// Generate the raw encoded sphinx packet to be included along
|
||||||
// with the htlcAdd message that we send directly to the
|
// with the htlcAdd message that we send directly to the
|
||||||
// switch.
|
// switch.
|
||||||
onionBlob, _, err := generateSphinxPacket(
|
onionBlob, err := attempt.OnionBlob()
|
||||||
&rt, attempt.Hash[:], attempt.SessionKey(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to create onion blob: attempt=%d in "+
|
log.Errorf("Failed to create onion blob: attempt=%d in "+
|
||||||
"payment=%v, err:%v", attempt.AttemptID,
|
"payment=%v, err:%v", attempt.AttemptID,
|
||||||
@ -714,7 +709,7 @@ func (p *paymentLifecycle) sendAttempt(
|
|||||||
return p.failAttempt(attempt.AttemptID, err)
|
return p.failAttempt(attempt.AttemptID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(htlcAdd.OnionBlob[:], onionBlob)
|
htlcAdd.OnionBlob = onionBlob
|
||||||
|
|
||||||
// Send it to the Switch. When this method returns we assume
|
// Send it to the Switch. When this method returns we assume
|
||||||
// the Switch successfully has persisted the payment attempt,
|
// the Switch successfully has persisted the payment attempt,
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
@ -15,7 +14,6 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
sphinx "github.com/lightningnetwork/lightning-onion"
|
|
||||||
"github.com/lightningnetwork/lnd/amp"
|
"github.com/lightningnetwork/lnd/amp"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/clock"
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
@ -722,71 +720,6 @@ func generateNewSessionKey() (*btcec.PrivateKey, error) {
|
|||||||
return btcec.NewPrivateKey()
|
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
|
// LightningPayment describes a payment to be sent through the network to the
|
||||||
// final destination.
|
// final destination.
|
||||||
type LightningPayment struct {
|
type LightningPayment struct {
|
||||||
|
@ -48,13 +48,6 @@ var (
|
|||||||
|
|
||||||
testFeatures = lnwire.NewFeatureVector(nil, lnwire.Features)
|
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)
|
testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
||||||
|
|
||||||
priv1, _ = btcec.NewPrivateKey()
|
priv1, _ = btcec.NewPrivateKey()
|
||||||
@ -1235,18 +1228,6 @@ func TestFindPathFeeWeighting(t *testing.T) {
|
|||||||
require.Equal(t, ctx.aliases["luoji"], path[0].policy.ToNodePubKey())
|
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
|
// TestUnknownErrorSource tests that if the source of an error is unknown, all
|
||||||
// edges along the route will be pruned.
|
// edges along the route will be pruned.
|
||||||
func TestUnknownErrorSource(t *testing.T) {
|
func TestUnknownErrorSource(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user