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

lightningnetwork / lnd / 14358372723

09 Apr 2025 01:26PM UTC coverage: 56.696% (-12.3%) from 69.037%
14358372723

Pull #9696

github

web-flow
Merge e2837e400 into 867d27d68
Pull Request #9696: Add `development_guidelines.md` for both human and machine

107055 of 188823 relevant lines covered (56.7%)

22721.56 hits per line

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

0.0
/lnrpc/rpc_utils.go
1
package lnrpc
2

3
import (
4
        "encoding/hex"
5
        "errors"
6
        "fmt"
7
        "sort"
8

9
        "github.com/btcsuite/btcd/chaincfg/chainhash"
10
        "github.com/lightningnetwork/lnd/lnwallet"
11
        "github.com/lightningnetwork/lnd/lnwallet/chainfee"
12
        "github.com/lightningnetwork/lnd/sweep"
13
        "google.golang.org/protobuf/encoding/protojson"
14
)
15

16
const (
17
        // RegisterRPCMiddlewareURI is the full RPC method URI for the
18
        // middleware registration call. This is declared here rather than where
19
        // it's mainly used to avoid circular package dependencies.
20
        RegisterRPCMiddlewareURI = "/lnrpc.Lightning/RegisterRPCMiddleware"
21
)
22

23
var (
24
        // ProtoJSONMarshalOpts is a struct that holds the default marshal
25
        // options for marshaling protobuf messages into JSON in a
26
        // human-readable way. This should only be used in the CLI and in
27
        // integration tests.
28
        ProtoJSONMarshalOpts = &protojson.MarshalOptions{
29
                EmitUnpopulated: true,
30
                UseProtoNames:   true,
31
                Indent:          "    ",
32
                UseHexForBytes:  true,
33
        }
34

35
        // ProtoJSONUnmarshalOpts is a struct that holds the default unmarshal
36
        // options for un-marshaling lncli JSON into protobuf messages. This
37
        // should only be used in the CLI and in integration tests.
38
        ProtoJSONUnmarshalOpts = &protojson.UnmarshalOptions{
39
                AllowPartial:   false,
40
                UseHexForBytes: true,
41
        }
42

43
        // RESTJsonMarshalOpts is a struct that holds the default marshal
44
        // options for marshaling protobuf messages into REST JSON in a
45
        // human-readable way. This should be used when interacting with the
46
        // REST proxy only.
47
        RESTJsonMarshalOpts = &protojson.MarshalOptions{
48
                EmitUnpopulated: true,
49
                UseProtoNames:   true,
50
        }
51

52
        // RESTJsonUnmarshalOpts is a struct that holds the default unmarshal
53
        // options for un-marshaling REST JSON into protobuf messages. This
54
        // should be used when interacting with the REST proxy only.
55
        RESTJsonUnmarshalOpts = &protojson.UnmarshalOptions{
56
                AllowPartial: false,
57
        }
58
)
59

60
// RPCTransaction returns a rpc transaction.
61
func RPCTransaction(tx *lnwallet.TransactionDetail) *Transaction {
×
62
        var destAddresses []string
×
63
        // Re-package destination output information.
×
64
        var outputDetails []*OutputDetail
×
65
        for _, o := range tx.OutputDetails {
×
66
                // Note: DestAddresses is deprecated but we keep
×
67
                // populating it with addresses for backwards
×
68
                // compatibility.
×
69
                for _, a := range o.Addresses {
×
70
                        destAddresses = append(destAddresses,
×
71
                                a.EncodeAddress())
×
72
                }
×
73

74
                var address string
×
75
                if len(o.Addresses) == 1 {
×
76
                        address = o.Addresses[0].EncodeAddress()
×
77
                }
×
78

79
                outputDetails = append(outputDetails, &OutputDetail{
×
80
                        OutputType:   MarshallOutputType(o.OutputType),
×
81
                        Address:      address,
×
82
                        PkScript:     hex.EncodeToString(o.PkScript),
×
83
                        OutputIndex:  int64(o.OutputIndex),
×
84
                        Amount:       int64(o.Value),
×
85
                        IsOurAddress: o.IsOurAddress,
×
86
                })
×
87
        }
88

89
        previousOutpoints := make([]*PreviousOutPoint, len(tx.PreviousOutpoints))
×
90
        for idx, previousOutPoint := range tx.PreviousOutpoints {
×
91
                previousOutpoints[idx] = &PreviousOutPoint{
×
92
                        Outpoint:    previousOutPoint.OutPoint,
×
93
                        IsOurOutput: previousOutPoint.IsOurOutput,
×
94
                }
×
95
        }
×
96

97
        // We also get unconfirmed transactions, so BlockHash can be nil.
98
        blockHash := ""
×
99
        if tx.BlockHash != nil {
×
100
                blockHash = tx.BlockHash.String()
×
101
        }
×
102

103
        return &Transaction{
×
104
                TxHash:            tx.Hash.String(),
×
105
                Amount:            int64(tx.Value),
×
106
                NumConfirmations:  tx.NumConfirmations,
×
107
                BlockHash:         blockHash,
×
108
                BlockHeight:       tx.BlockHeight,
×
109
                TimeStamp:         tx.Timestamp,
×
110
                TotalFees:         tx.TotalFees,
×
111
                DestAddresses:     destAddresses,
×
112
                OutputDetails:     outputDetails,
×
113
                RawTxHex:          hex.EncodeToString(tx.RawTx),
×
114
                Label:             tx.Label,
×
115
                PreviousOutpoints: previousOutpoints,
×
116
        }
×
117
}
118

119
// RPCTransactionDetails returns a set of rpc transaction details.
120
func RPCTransactionDetails(txns []*lnwallet.TransactionDetail, firstIdx,
121
        lastIdx uint64) *TransactionDetails {
×
122

×
123
        txDetails := &TransactionDetails{
×
124
                Transactions: make([]*Transaction, len(txns)),
×
125
                FirstIndex:   firstIdx,
×
126
                LastIndex:    lastIdx,
×
127
        }
×
128

×
129
        for i, tx := range txns {
×
130
                txDetails.Transactions[i] = RPCTransaction(tx)
×
131
        }
×
132

133
        // Sort transactions by number of confirmations rather than height so
134
        // that unconfirmed transactions (height =0; confirmations =-1) will
135
        // follow the most recently set of confirmed transactions. If we sort
136
        // by height, unconfirmed transactions will follow our oldest
137
        // transactions, because they have lower block heights.
138
        sort.Slice(txDetails.Transactions, func(i, j int) bool {
×
139
                return txDetails.Transactions[i].NumConfirmations <
×
140
                        txDetails.Transactions[j].NumConfirmations
×
141
        })
×
142

143
        return txDetails
×
144
}
145

146
// ExtractMinConfs extracts the minimum number of confirmations that each
147
// output used to fund a transaction should satisfy.
148
func ExtractMinConfs(minConfs int32, spendUnconfirmed bool) (int32, error) {
×
149
        switch {
×
150
        // Ensure that the MinConfs parameter is non-negative.
151
        case minConfs < 0:
×
152
                return 0, errors.New("minimum number of confirmations must " +
×
153
                        "be a non-negative number")
×
154

155
        // The transaction should not be funded with unconfirmed outputs
156
        // unless explicitly specified by SpendUnconfirmed. We do this to
157
        // provide sane defaults to the OpenChannel RPC, as otherwise, if the
158
        // MinConfs field isn't explicitly set by the caller, we'll use
159
        // unconfirmed outputs without the caller being aware.
160
        case minConfs == 0 && !spendUnconfirmed:
×
161
                return 1, nil
×
162

163
        // In the event that the caller set MinConfs > 0 and SpendUnconfirmed to
164
        // true, we'll return an error to indicate the conflict.
165
        case minConfs > 0 && spendUnconfirmed:
×
166
                return 0, errors.New("SpendUnconfirmed set to true with " +
×
167
                        "MinConfs > 0")
×
168

169
        // The funding transaction of the new channel to be created can be
170
        // funded with unconfirmed outputs.
171
        case spendUnconfirmed:
×
172
                return 0, nil
×
173

174
        // If none of the above cases matched, we'll return the value set
175
        // explicitly by the caller.
176
        default:
×
177
                return minConfs, nil
×
178
        }
179
}
180

181
// GetChanPointFundingTxid returns the given channel point's funding txid in
182
// raw bytes.
183
func GetChanPointFundingTxid(chanPoint *ChannelPoint) (*chainhash.Hash, error) {
×
184
        var txid []byte
×
185

×
186
        // A channel point's funding txid can be get/set as a byte slice or a
×
187
        // string. In the case it is a string, decode it.
×
188
        switch chanPoint.GetFundingTxid().(type) {
×
189
        case *ChannelPoint_FundingTxidBytes:
×
190
                txid = chanPoint.GetFundingTxidBytes()
×
191
        case *ChannelPoint_FundingTxidStr:
×
192
                s := chanPoint.GetFundingTxidStr()
×
193
                h, err := chainhash.NewHashFromStr(s)
×
194
                if err != nil {
×
195
                        return nil, err
×
196
                }
×
197

198
                txid = h[:]
×
199
        }
200

201
        return chainhash.NewHash(txid)
×
202
}
203

204
// GetChannelOutPoint returns the outpoint of the related channel point.
205
func GetChannelOutPoint(chanPoint *ChannelPoint) (*OutPoint, error) {
×
206
        var txid []byte
×
207

×
208
        // A channel point's funding txid can be get/set as a byte slice or a
×
209
        // string. In the case it is a string, decode it.
×
210
        switch chanPoint.GetFundingTxid().(type) {
×
211
        case *ChannelPoint_FundingTxidBytes:
×
212
                txid = chanPoint.GetFundingTxidBytes()
×
213

214
        case *ChannelPoint_FundingTxidStr:
×
215
                s := chanPoint.GetFundingTxidStr()
×
216
                h, err := chainhash.NewHashFromStr(s)
×
217
                if err != nil {
×
218
                        return nil, err
×
219
                }
×
220

221
                txid = h[:]
×
222
        }
223

224
        return &OutPoint{
×
225
                TxidBytes:   txid,
×
226
                OutputIndex: chanPoint.OutputIndex,
×
227
        }, nil
×
228
}
229

230
// CalculateFeeRate uses either satPerByte or satPerVByte, but not both, from a
231
// request to calculate the fee rate. It provides compatibility for the
232
// deprecated field, satPerByte. Once the field is safe to be removed, the
233
// check can then be deleted.
234
func CalculateFeeRate(satPerByte, satPerVByte uint64, targetConf uint32,
235
        estimator chainfee.Estimator) (chainfee.SatPerKWeight, error) {
×
236

×
237
        var feeRate chainfee.SatPerKWeight
×
238

×
239
        // We only allow using either the deprecated field or the new field.
×
240
        if satPerByte != 0 && satPerVByte != 0 {
×
241
                return feeRate, fmt.Errorf("either SatPerByte or " +
×
242
                        "SatPerVByte should be set, but not both")
×
243
        }
×
244

245
        // Default to satPerVByte, and overwrite it if satPerByte is set.
246
        satPerKw := chainfee.SatPerKVByte(satPerVByte * 1000).FeePerKWeight()
×
247
        if satPerByte != 0 {
×
248
                satPerKw = chainfee.SatPerKVByte(
×
249
                        satPerByte * 1000,
×
250
                ).FeePerKWeight()
×
251
        }
×
252

253
        // Based on the passed fee related parameters, we'll determine an
254
        // appropriate fee rate for this transaction.
255
        feePref := sweep.FeeEstimateInfo{
×
256
                ConfTarget: targetConf,
×
257
                FeeRate:    satPerKw,
×
258
        }
×
259
        // TODO(yy): need to pass the configured max fee here.
×
260
        feeRate, err := feePref.Estimate(estimator, 0)
×
261
        if err != nil {
×
262
                return feeRate, err
×
263
        }
×
264

265
        return feeRate, nil
×
266
}
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