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

lightningnetwork / lnd / 9915780197

13 Jul 2024 12:30AM UTC coverage: 49.268% (-9.1%) from 58.413%
9915780197

push

github

web-flow
Merge pull request #8653 from ProofOfKeags/fn-prim

DynComms [0/n]: `fn` package additions

92837 of 188433 relevant lines covered (49.27%)

1.55 hits per line

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

90.65
/lnwallet/transactions.go
1
package lnwallet
2

3
import (
4
        "encoding/binary"
5
        "fmt"
6

7
        "github.com/btcsuite/btcd/btcec/v2"
8
        "github.com/btcsuite/btcd/btcutil"
9
        "github.com/btcsuite/btcd/wire"
10
        "github.com/lightningnetwork/lnd/channeldb"
11
)
12

13
const (
14
        // StateHintSize is the total number of bytes used between the sequence
15
        // number and locktime of the commitment transaction use to encode a hint
16
        // to the state number of a particular commitment transaction.
17
        StateHintSize = 6
18

19
        // MaxStateHint is the maximum state number we're able to encode using
20
        // StateHintSize bytes amongst the sequence number and locktime fields
21
        // of the commitment transaction.
22
        maxStateHint uint64 = (1 << 48) - 1
23
)
24

25
var (
26
        // TimelockShift is used to make sure the commitment transaction is
27
        // spendable by setting the locktime with it so that it is larger than
28
        // 500,000,000, thus interpreting it as Unix epoch timestamp and not
29
        // a block height. It is also smaller than the current timestamp which
30
        // has bit (1 << 30) set, so there is no risk of having the commitment
31
        // transaction be rejected. This way we can safely use the lower 24 bits
32
        // of the locktime field for part of the obscured commitment transaction
33
        // number.
34
        TimelockShift = uint32(1 << 29)
35
)
36

37
// CreateHtlcSuccessTx creates a transaction that spends the output on the
38
// commitment transaction of the peer that receives an HTLC. This transaction
39
// essentially acts as an off-chain covenant as it's only permitted to spend
40
// the designated HTLC output, and also that spend can _only_ be used as a
41
// state transition to create another output which actually allows redemption
42
// or revocation of an HTLC.
43
//
44
// In order to spend the segwit v0 HTLC output, the witness for the passed
45
// transaction should be:
46
//   - <0> <sender sig> <recvr sig> <preimage>
47
//
48
// In order to spend the segwit v1 (taproot) HTLC output, the witness for the
49
// passed transaction should be:
50
//   - <sender sig> <receiver sig> <preimage> <success_script> <control_block>
51
func CreateHtlcSuccessTx(chanType channeldb.ChannelType, initiator bool,
52
        htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, csvDelay,
53
        leaseExpiry uint32, revocationKey, delayKey *btcec.PublicKey) (
54
        *wire.MsgTx, error) {
3✔
55

3✔
56
        // Create a version two transaction (as the success version of this
3✔
57
        // spends an output with a CSV timeout).
3✔
58
        successTx := wire.NewMsgTx(2)
3✔
59

3✔
60
        // The input to the transaction is the outpoint that creates the
3✔
61
        // original HTLC on the sender's commitment transaction. Set the
3✔
62
        // sequence number based on the channel type.
3✔
63
        txin := &wire.TxIn{
3✔
64
                PreviousOutPoint: htlcOutput,
3✔
65
                Sequence:         HtlcSecondLevelInputSequence(chanType),
3✔
66
        }
3✔
67
        successTx.AddTxIn(txin)
3✔
68

3✔
69
        // Next, we'll generate the script used as the output for all second
3✔
70
        // level HTLC which forces a covenant w.r.t what can be done with all
3✔
71
        // HTLC outputs.
3✔
72
        scriptInfo, err := SecondLevelHtlcScript(
3✔
73
                chanType, initiator, revocationKey, delayKey, csvDelay,
3✔
74
                leaseExpiry,
3✔
75
        )
3✔
76
        if err != nil {
3✔
77
                return nil, err
×
78
        }
×
79

80
        // Finally, the output is simply the amount of the HTLC (minus the
81
        // required fees), paying to the timeout script.
82
        successTx.AddTxOut(&wire.TxOut{
3✔
83
                Value:    int64(htlcAmt),
3✔
84
                PkScript: scriptInfo.PkScript(),
3✔
85
        })
3✔
86

3✔
87
        return successTx, nil
3✔
88
}
89

90
// CreateHtlcTimeoutTx creates a transaction that spends the HTLC output on the
91
// commitment transaction of the peer that created an HTLC (the sender). This
92
// transaction essentially acts as an off-chain covenant as it spends a 2-of-2
93
// multi-sig output. This output requires a signature from both the sender and
94
// receiver of the HTLC. By using a distinct transaction, we're able to
95
// uncouple the timeout and delay clauses of the HTLC contract. This
96
// transaction is locked with an absolute lock-time so the sender can only
97
// attempt to claim the output using it after the lock time has passed.
98
//
99
// In order to spend the HTLC output for segwit v0, the witness for the passed
100
// transaction should be:
101
//   - <0> <sender sig> <receiver sig> <0>
102
//
103
// In order to spend the HTLC output for segwit v1, then witness for the passed
104
// transaction should be:
105
//   - <sender sig> <receiver sig> <timeout_script> <control_block>
106
//
107
// NOTE: The passed amount for the HTLC should take into account the required
108
// fee rate at the time the HTLC was created. The fee should be able to
109
// entirely pay for this (tiny: 1-in 1-out) transaction.
110
func CreateHtlcTimeoutTx(chanType channeldb.ChannelType, initiator bool,
111
        htlcOutput wire.OutPoint, htlcAmt btcutil.Amount,
112
        cltvExpiry, csvDelay, leaseExpiry uint32,
113
        revocationKey, delayKey *btcec.PublicKey) (*wire.MsgTx, error) {
3✔
114

3✔
115
        // Create a version two transaction (as the success version of this
3✔
116
        // spends an output with a CSV timeout), and set the lock-time to the
3✔
117
        // specified absolute lock-time in blocks.
3✔
118
        timeoutTx := wire.NewMsgTx(2)
3✔
119
        timeoutTx.LockTime = cltvExpiry
3✔
120

3✔
121
        // The input to the transaction is the outpoint that creates the
3✔
122
        // original HTLC on the sender's commitment transaction. Set the
3✔
123
        // sequence number based on the channel type.
3✔
124
        txin := &wire.TxIn{
3✔
125
                PreviousOutPoint: htlcOutput,
3✔
126
                SignatureScript:  []byte{},
3✔
127
                Witness:          [][]byte{},
3✔
128
                Sequence:         HtlcSecondLevelInputSequence(chanType),
3✔
129
        }
3✔
130
        timeoutTx.AddTxIn(txin)
3✔
131

3✔
132
        // Next, we'll generate the script used as the output for all second
3✔
133
        // level HTLC which forces a covenant w.r.t what can be done with all
3✔
134
        // HTLC outputs.
3✔
135
        scriptInfo, err := SecondLevelHtlcScript(
3✔
136
                chanType, initiator, revocationKey, delayKey, csvDelay,
3✔
137
                leaseExpiry,
3✔
138
        )
3✔
139
        if err != nil {
3✔
140
                return nil, err
×
141
        }
×
142

143
        // Finally, the output is simply the amount of the HTLC (minus the
144
        // required fees), paying to the regular second level HTLC script.
145
        timeoutTx.AddTxOut(&wire.TxOut{
3✔
146
                Value:    int64(htlcAmt),
3✔
147
                PkScript: scriptInfo.PkScript(),
3✔
148
        })
3✔
149

3✔
150
        return timeoutTx, nil
3✔
151
}
152

153
// SetStateNumHint encodes the current state number within the passed
154
// commitment transaction by re-purposing the locktime and sequence fields in
155
// the commitment transaction to encode the obfuscated state number.  The state
156
// number is encoded using 48 bits. The lower 24 bits of the lock time are the
157
// lower 24 bits of the obfuscated state number and the lower 24 bits of the
158
// sequence field are the higher 24 bits. Finally before encoding, the
159
// obfuscator is XOR'd against the state number in order to hide the exact
160
// state number from the PoV of outside parties.
161
func SetStateNumHint(commitTx *wire.MsgTx, stateNum uint64,
162
        obfuscator [StateHintSize]byte) error {
3✔
163

3✔
164
        // With the current schema we are only able to encode state num
3✔
165
        // hints up to 2^48. Therefore if the passed height is greater than our
3✔
166
        // state hint ceiling, then exit early.
3✔
167
        if stateNum > maxStateHint {
3✔
168
                return fmt.Errorf("unable to encode state, %v is greater "+
×
169
                        "state num that max of %v", stateNum, maxStateHint)
×
170
        }
×
171

172
        if len(commitTx.TxIn) != 1 {
3✔
173
                return fmt.Errorf("commitment tx must have exactly 1 input, "+
×
174
                        "instead has %v", len(commitTx.TxIn))
×
175
        }
×
176

177
        // Convert the obfuscator into a uint64, then XOR that against the
178
        // targeted height in order to obfuscate the state number of the
179
        // commitment transaction in the case that either commitment
180
        // transaction is broadcast directly on chain.
181
        var obfs [8]byte
3✔
182
        copy(obfs[2:], obfuscator[:])
3✔
183
        xorInt := binary.BigEndian.Uint64(obfs[:])
3✔
184

3✔
185
        stateNum = stateNum ^ xorInt
3✔
186

3✔
187
        // Set the height bit of the sequence number in order to disable any
3✔
188
        // sequence locks semantics.
3✔
189
        commitTx.TxIn[0].Sequence = uint32(stateNum>>24) | wire.SequenceLockTimeDisabled
3✔
190
        commitTx.LockTime = uint32(stateNum&0xFFFFFF) | TimelockShift
3✔
191

3✔
192
        return nil
3✔
193
}
194

195
// GetStateNumHint recovers the current state number given a commitment
196
// transaction which has previously had the state number encoded within it via
197
// setStateNumHint and a shared obfuscator.
198
//
199
// See setStateNumHint for further details w.r.t exactly how the state-hints
200
// are encoded.
201
func GetStateNumHint(commitTx *wire.MsgTx, obfuscator [StateHintSize]byte) uint64 {
3✔
202
        // Convert the obfuscator into a uint64, this will be used to
3✔
203
        // de-obfuscate the final recovered state number.
3✔
204
        var obfs [8]byte
3✔
205
        copy(obfs[2:], obfuscator[:])
3✔
206
        xorInt := binary.BigEndian.Uint64(obfs[:])
3✔
207

3✔
208
        // Retrieve the state hint from the sequence number and locktime
3✔
209
        // of the transaction.
3✔
210
        stateNumXor := uint64(commitTx.TxIn[0].Sequence&0xFFFFFF) << 24
3✔
211
        stateNumXor |= uint64(commitTx.LockTime & 0xFFFFFF)
3✔
212

3✔
213
        // Finally, to obtain the final state number, we XOR by the obfuscator
3✔
214
        // value to de-obfuscate the state number.
3✔
215
        return stateNumXor ^ xorInt
3✔
216
}
3✔
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