• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.77
/amp/shard_tracker.go
1
package amp
2

3
import (
4
        "crypto/rand"
5
        "encoding/binary"
6
        "fmt"
7
        "sync"
8

9
        "github.com/lightningnetwork/lnd/lntypes"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
        "github.com/lightningnetwork/lnd/record"
12
        "github.com/lightningnetwork/lnd/routing/shards"
13
)
14

15
// Shard is an implementation of the shards.PaymentShards interface specific
16
// to AMP payments.
17
type Shard struct {
18
        child *Child
19
        mpp   *record.MPP
20
        amp   *record.AMP
21
}
22

23
// A compile time check to ensure Shard implements the shards.PaymentShard
24
// interface.
25
var _ shards.PaymentShard = (*Shard)(nil)
26

27
// Hash returns the hash used for the HTLC representing this AMP shard.
28
func (s *Shard) Hash() lntypes.Hash {
3✔
29
        return s.child.Hash
3✔
30
}
3✔
31

32
// MPP returns any extra MPP records that should be set for the final hop on
33
// the route used by this shard.
34
func (s *Shard) MPP() *record.MPP {
3✔
35
        return s.mpp
3✔
36
}
3✔
37

38
// AMP returns any extra AMP records that should be set for the final hop on
39
// the route used by this shard.
40
func (s *Shard) AMP() *record.AMP {
3✔
41
        return s.amp
3✔
42
}
3✔
43

44
// ShardTracker is an implementation of the shards.ShardTracker interface
45
// that is able to generate payment shards according to the AMP splitting
46
// algorithm. It can be used to generate new hashes to use for HTLCs, and also
47
// cancel shares used for failed payment shards.
48
type ShardTracker struct {
49
        setID       [32]byte
50
        paymentAddr [32]byte
51
        totalAmt    lnwire.MilliSatoshi
52

53
        sharer Sharer
54

55
        shards map[uint64]*Child
56
        sync.Mutex
57
}
58

59
// A compile time check to ensure ShardTracker implements the
60
// shards.ShardTracker interface.
61
var _ shards.ShardTracker = (*ShardTracker)(nil)
62

63
// NewShardTracker creates a new shard tracker to use for AMP payments. The
64
// root shard, setID, payment address and total amount must be correctly set in
65
// order for the TLV options to include with each shard to be created
66
// correctly.
67
func NewShardTracker(root, setID, payAddr [32]byte,
68
        totalAmt lnwire.MilliSatoshi) *ShardTracker {
3✔
69

3✔
70
        // Create a new seed sharer from this root.
3✔
71
        rootShare := Share(root)
3✔
72
        rootSharer := SeedSharerFromRoot(&rootShare)
3✔
73

3✔
74
        return &ShardTracker{
3✔
75
                setID:       setID,
3✔
76
                paymentAddr: payAddr,
3✔
77
                totalAmt:    totalAmt,
3✔
78
                sharer:      rootSharer,
3✔
79
                shards:      make(map[uint64]*Child),
3✔
80
        }
3✔
81
}
3✔
82

83
// NewShard registers a new attempt with the ShardTracker and returns a
84
// new shard representing this attempt. This attempt's shard should be canceled
85
// if it ends up not being used by the overall payment, i.e. if the attempt
86
// fails.
87
func (s *ShardTracker) NewShard(pid uint64, last bool) (shards.PaymentShard,
88
        error) {
3✔
89

3✔
90
        s.Lock()
3✔
91
        defer s.Unlock()
3✔
92

3✔
93
        // Use a random child index.
3✔
94
        var childIndex [4]byte
3✔
95
        if _, err := rand.Read(childIndex[:]); err != nil {
3✔
96
                return nil, err
×
97
        }
×
98
        idx := binary.BigEndian.Uint32(childIndex[:])
3✔
99

3✔
100
        // Depending on whether we are requesting the last shard or not, either
3✔
101
        // split the current share into two, or get a Child directly from the
3✔
102
        // current sharer.
3✔
103
        var child *Child
3✔
104
        if last {
6✔
105
                child = s.sharer.Child(idx)
3✔
106

3✔
107
                // If this was the last shard, set the current share to the
3✔
108
                // zero share to indicate we cannot split it further.
3✔
109
                s.sharer = s.sharer.Zero()
3✔
110
        } else {
6✔
111
                left, sharer, err := s.sharer.Split()
3✔
112
                if err != nil {
3✔
113
                        return nil, err
×
114
                }
×
115

116
                s.sharer = sharer
3✔
117
                child = left.Child(idx)
3✔
118
        }
119

120
        // Track the new child and return the shard.
121
        s.shards[pid] = child
3✔
122

3✔
123
        mpp := record.NewMPP(s.totalAmt, s.paymentAddr)
3✔
124
        amp := record.NewAMP(
3✔
125
                child.ChildDesc.Share, s.setID, child.ChildDesc.Index,
3✔
126
        )
3✔
127

3✔
128
        return &Shard{
3✔
129
                child: child,
3✔
130
                mpp:   mpp,
3✔
131
                amp:   amp,
3✔
132
        }, nil
3✔
133
}
134

135
// CancelShard cancel's the shard corresponding to the given attempt ID.
136
func (s *ShardTracker) CancelShard(pid uint64) error {
3✔
137
        s.Lock()
3✔
138
        defer s.Unlock()
3✔
139

3✔
140
        c, ok := s.shards[pid]
3✔
141
        if !ok {
3✔
142
                return fmt.Errorf("pid not found")
×
143
        }
×
144
        delete(s.shards, pid)
3✔
145

3✔
146
        // Now that we are canceling this shard, we XOR the share back into our
3✔
147
        // current share.
3✔
148
        s.sharer = s.sharer.Merge(c)
3✔
149
        return nil
3✔
150
}
151

152
// GetHash retrieves the hash used by the shard of the given attempt ID. This
153
// will return an error if the attempt ID is unknown.
154
func (s *ShardTracker) GetHash(pid uint64) (lntypes.Hash, error) {
3✔
155
        s.Lock()
3✔
156
        defer s.Unlock()
3✔
157

3✔
158
        c, ok := s.shards[pid]
3✔
159
        if !ok {
3✔
UNCOV
160
                return lntypes.Hash{}, fmt.Errorf("AMP shard for attempt %v "+
×
UNCOV
161
                        "not found", pid)
×
UNCOV
162
        }
×
163

164
        return c.Hash, nil
3✔
165
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc