Skip to content

Commit f5ee874

Browse files
authored
Merge pull request #5305 from carlaKC/sessionkey-deserialize
channeldb: read HtlcAttemptInfo session key raw bytes
2 parents c18f333 + 64333c0 commit f5ee874

8 files changed

+95
-40
lines changed

channeldb/duplicate_payments.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ type duplicateHTLCAttemptInfo struct {
5353
attemptID uint64
5454

5555
// sessionKey is the ephemeral key used for this attempt.
56-
sessionKey *btcec.PrivateKey
56+
sessionKey [btcec.PrivKeyBytesLen]byte
5757

5858
// route is the route attempted to send the HTLC.
5959
route route.Route
@@ -181,7 +181,7 @@ func fetchDuplicatePayment(bucket kvdb.RBucket) (*MPPayment, error) {
181181
HTLCAttemptInfo: HTLCAttemptInfo{
182182
AttemptID: attempt.attemptID,
183183
Route: attempt.route,
184-
SessionKey: attempt.sessionKey,
184+
sessionKey: attempt.sessionKey,
185185
},
186186
}
187187

channeldb/mp_payment.go

+39-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,15 @@ type HTLCAttemptInfo struct {
2121
// AttemptID is the unique ID used for this attempt.
2222
AttemptID uint64
2323

24-
// SessionKey is the ephemeral key used for this attempt.
25-
SessionKey *btcec.PrivateKey
24+
// sessionKey is the raw bytes ephemeral key used for this attempt.
25+
// These bytes are lazily read off disk to save ourselves the expensive
26+
// EC operations used by btcec.PrivKeyFromBytes.
27+
sessionKey [btcec.PrivKeyBytesLen]byte
28+
29+
// cachedSessionKey is our fully deserialized sesionKey. This value
30+
// may be nil if the attempt has just been read from disk and its
31+
// session key has not been used yet.
32+
cachedSessionKey *btcec.PrivateKey
2633

2734
// Route is the route attempted to send the HTLC.
2835
Route route.Route
@@ -38,6 +45,36 @@ type HTLCAttemptInfo struct {
3845
Hash *lntypes.Hash
3946
}
4047

48+
// NewHtlcAttemptInfo creates a htlc attempt.
49+
func NewHtlcAttemptInfo(attemptID uint64, sessionKey *btcec.PrivateKey,
50+
route route.Route, attemptTime time.Time,
51+
hash *lntypes.Hash) *HTLCAttemptInfo {
52+
53+
var scratch [btcec.PrivKeyBytesLen]byte
54+
copy(scratch[:], sessionKey.Serialize())
55+
56+
return &HTLCAttemptInfo{
57+
AttemptID: attemptID,
58+
sessionKey: scratch,
59+
cachedSessionKey: sessionKey,
60+
Route: route,
61+
AttemptTime: attemptTime,
62+
Hash: hash,
63+
}
64+
}
65+
66+
// SessionKey returns the ephemeral key used for a htlc attempt. This function
67+
// performs expensive ec-ops to obtain the session key if it is not cached.
68+
func (h *HTLCAttemptInfo) SessionKey() *btcec.PrivateKey {
69+
if h.cachedSessionKey == nil {
70+
h.cachedSessionKey, _ = btcec.PrivKeyFromBytes(
71+
btcec.S256(), h.sessionKey[:],
72+
)
73+
}
74+
75+
return h.cachedSessionKey
76+
}
77+
4178
// HTLCAttempt contains information about a specific HTLC attempt for a given
4279
// payment. It contains the HTLCAttemptInfo used to send the HTLC, as well
4380
// as a timestamp and any known outcome of the attempt.

channeldb/mp_payment_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package channeldb
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
// TestLazySessionKeyDeserialize tests that we can read htlc attempt session
11+
// keys that were previously serialized as a private key as raw bytes.
12+
func TestLazySessionKeyDeserialize(t *testing.T) {
13+
var b bytes.Buffer
14+
15+
// Serialize as a private key.
16+
err := WriteElements(&b, priv)
17+
require.NoError(t, err)
18+
19+
// Deserialize into [btcec.PrivKeyBytesLen]byte.
20+
attempt := HTLCAttemptInfo{}
21+
err = ReadElements(&b, &attempt.sessionKey)
22+
require.NoError(t, err)
23+
require.Zero(t, b.Len())
24+
25+
sessionKey := attempt.SessionKey()
26+
require.Equal(t, priv, sessionKey)
27+
}

channeldb/payment_control_test.go

+8-10
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,15 @@ func genInfo() (*PaymentCreationInfo, *HTLCAttemptInfo,
3737
}
3838

3939
rhash := sha256.Sum256(preimage[:])
40+
attempt := NewHtlcAttemptInfo(
41+
0, priv, *testRoute.Copy(), time.Time{}, nil,
42+
)
4043
return &PaymentCreationInfo{
41-
PaymentIdentifier: rhash,
42-
Value: testRoute.ReceiverAmt(),
43-
CreationTime: time.Unix(time.Now().Unix(), 0),
44-
PaymentRequest: []byte("hola"),
45-
},
46-
&HTLCAttemptInfo{
47-
AttemptID: 0,
48-
SessionKey: priv,
49-
Route: *testRoute.Copy(),
50-
}, preimage, nil
44+
PaymentIdentifier: rhash,
45+
Value: testRoute.ReceiverAmt(),
46+
CreationTime: time.Unix(time.Now().Unix(), 0),
47+
PaymentRequest: []byte("hola"),
48+
}, attempt, preimage, nil
5149
}
5250

5351
// TestPaymentControlSwitchFail checks that payment status returns to Failed

channeldb/payments.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ func deserializePaymentCreationInfo(r io.Reader) (*PaymentCreationInfo, error) {
919919
}
920920

921921
func serializeHTLCAttemptInfo(w io.Writer, a *HTLCAttemptInfo) error {
922-
if err := WriteElements(w, a.SessionKey); err != nil {
922+
if err := WriteElements(w, a.sessionKey); err != nil {
923923
return err
924924
}
925925

@@ -945,7 +945,7 @@ func serializeHTLCAttemptInfo(w io.Writer, a *HTLCAttemptInfo) error {
945945

946946
func deserializeHTLCAttemptInfo(r io.Reader) (*HTLCAttemptInfo, error) {
947947
a := &HTLCAttemptInfo{}
948-
err := ReadElements(r, &a.SessionKey)
948+
err := ReadElements(r, &a.sessionKey)
949949
if err != nil {
950950
return nil, err
951951
}

channeldb/payments_test.go

+8-9
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,10 @@ func makeFakeInfo() (*PaymentCreationInfo, *HTLCAttemptInfo) {
6868
PaymentRequest: []byte(""),
6969
}
7070

71-
a := &HTLCAttemptInfo{
72-
AttemptID: 44,
73-
SessionKey: priv,
74-
Route: testRoute,
75-
AttemptTime: time.Unix(100, 0),
76-
Hash: &hash,
77-
}
71+
a := NewHtlcAttemptInfo(
72+
44, priv, testRoute, time.Unix(100, 0), &hash,
73+
)
74+
7875
return c, a
7976
}
8077

@@ -123,9 +120,11 @@ func TestSentPaymentSerialization(t *testing.T) {
123120
newWireInfo.Route = route.Route{}
124121
s.Route = route.Route{}
125122

123+
// Call session key method to set our cached session key so we can use
124+
// DeepEqual, and assert that our key equals the original key.
125+
require.Equal(t, s.cachedSessionKey, newWireInfo.SessionKey())
126+
126127
if !reflect.DeepEqual(s, newWireInfo) {
127-
s.SessionKey.Curve = nil
128-
newWireInfo.SessionKey.Curve = nil
129128
t.Fatalf("Payments do not match after "+
130129
"serialization/deserialization %v vs %v",
131130
spew.Sdump(s), spew.Sdump(newWireInfo),

routing/control_tower_test.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -331,11 +331,9 @@ func genInfo() (*channeldb.PaymentCreationInfo, *channeldb.HTLCAttemptInfo,
331331
CreationTime: time.Unix(time.Now().Unix(), 0),
332332
PaymentRequest: []byte("hola"),
333333
},
334-
&channeldb.HTLCAttemptInfo{
335-
AttemptID: 1,
336-
SessionKey: priv,
337-
Route: testRoute,
338-
}, preimage, nil
334+
channeldb.NewHtlcAttemptInfo(
335+
1, priv, testRoute, time.Time{}, nil,
336+
), preimage, nil
339337
}
340338

341339
func genPreimage() ([32]byte, error) {

routing/payment_lifecycle.go

+6-10
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ func (p *shardHandler) collectResult(attempt *channeldb.HTLCAttemptInfo) (
499499

500500
// Regenerate the circuit for this attempt.
501501
_, circuit, err := generateSphinxPacket(
502-
&attempt.Route, hash[:], attempt.SessionKey,
502+
&attempt.Route, hash[:], attempt.SessionKey(),
503503
)
504504
if err != nil {
505505
return nil, err
@@ -677,15 +677,11 @@ func (p *shardHandler) createNewPaymentAttempt(rt *route.Route, lastShard bool)
677677
rt.Hops[0].ChannelID,
678678
)
679679

680-
// We now have all the information needed to populate
681-
// the current attempt information.
682-
attempt := &channeldb.HTLCAttemptInfo{
683-
AttemptID: attemptID,
684-
AttemptTime: p.router.cfg.Clock.Now(),
685-
SessionKey: sessionKey,
686-
Route: *rt,
687-
Hash: &hash,
688-
}
680+
// We now have all the information needed to populate the current
681+
// attempt information.
682+
attempt := channeldb.NewHtlcAttemptInfo(
683+
attemptID, sessionKey, *rt, p.router.cfg.Clock.Now(), &hash,
684+
)
689685

690686
return firstHop, htlcAdd, attempt, nil
691687
}

0 commit comments

Comments
 (0)