2022-08-12 11:03:44 +02:00
|
|
|
package lntest
|
2022-07-22 02:41:08 +02:00
|
|
|
|
|
|
|
import (
|
2022-07-22 11:33:26 +02:00
|
|
|
"fmt"
|
2022-07-22 02:41:08 +02:00
|
|
|
"io"
|
2022-07-26 10:59:57 +02:00
|
|
|
"math"
|
2022-07-22 02:41:08 +02:00
|
|
|
"os"
|
2022-08-18 11:30:34 +02:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2022-07-22 02:41:08 +02:00
|
|
|
|
2022-08-12 10:02:32 +02:00
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
2022-08-03 21:38:09 +02:00
|
|
|
"github.com/btcsuite/btcd/wire"
|
2022-08-12 10:02:32 +02:00
|
|
|
"github.com/lightningnetwork/lnd/input"
|
2022-08-03 20:36:12 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
2022-08-12 07:07:16 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
2022-08-12 10:02:32 +02:00
|
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
|
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
2022-07-22 02:41:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// NeutrinoBackendName is the name of the neutrino backend.
|
|
|
|
NeutrinoBackendName = "neutrino"
|
|
|
|
|
2022-08-12 07:07:16 +02:00
|
|
|
DefaultTimeout = wait.DefaultTimeout
|
2022-07-26 10:59:57 +02:00
|
|
|
|
|
|
|
// noFeeLimitMsat is used to specify we will put no requirements on fee
|
|
|
|
// charged when choosing a route path.
|
|
|
|
noFeeLimitMsat = math.MaxInt64
|
2022-07-22 02:41:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// CopyFile copies the file src to dest.
|
|
|
|
func CopyFile(dest, src string) error {
|
|
|
|
s, err := os.Open(src)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer s.Close()
|
|
|
|
|
|
|
|
d, err := os.Create(dest)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := io.Copy(d, s); err != nil {
|
|
|
|
d.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return d.Close()
|
|
|
|
}
|
2022-07-22 11:33:26 +02:00
|
|
|
|
|
|
|
// errNumNotMatched is a helper method to return a nicely formatted error.
|
|
|
|
func errNumNotMatched(name string, subject string,
|
|
|
|
want, got, total, old int) error {
|
|
|
|
|
|
|
|
return fmt.Errorf("%s: assert %s failed: want %d, got: %d, total: "+
|
|
|
|
"%d, previously had: %d", name, subject, want, got, total, old)
|
|
|
|
}
|
2022-08-18 11:30:34 +02:00
|
|
|
|
|
|
|
// parseDerivationPath parses a path in the form of m/x'/y'/z'/a/b into a slice
|
|
|
|
// of [x, y, z, a, b], meaning that the apostrophe is ignored and 2^31 is _not_
|
|
|
|
// added to the numbers.
|
|
|
|
func ParseDerivationPath(path string) ([]uint32, error) {
|
|
|
|
path = strings.TrimSpace(path)
|
|
|
|
if len(path) == 0 {
|
|
|
|
return nil, fmt.Errorf("path cannot be empty")
|
|
|
|
}
|
|
|
|
if !strings.HasPrefix(path, "m/") {
|
|
|
|
return nil, fmt.Errorf("path must start with m/")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just the root key, no path was provided. This is valid but not useful
|
|
|
|
// in most cases.
|
|
|
|
rest := strings.ReplaceAll(path, "m/", "")
|
|
|
|
if rest == "" {
|
|
|
|
return []uint32{}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
parts := strings.Split(rest, "/")
|
|
|
|
indices := make([]uint32, len(parts))
|
|
|
|
for i := 0; i < len(parts); i++ {
|
|
|
|
part := parts[i]
|
|
|
|
if strings.Contains(parts[i], "'") {
|
|
|
|
part = strings.TrimRight(parts[i], "'")
|
|
|
|
}
|
|
|
|
parsed, err := strconv.ParseInt(part, 10, 32)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("could not parse part \"%s\": "+
|
|
|
|
"%v", part, err)
|
|
|
|
}
|
|
|
|
indices[i] = uint32(parsed)
|
|
|
|
}
|
|
|
|
|
|
|
|
return indices, nil
|
|
|
|
}
|
2022-08-03 20:36:12 +02:00
|
|
|
|
|
|
|
// ChanPointFromPendingUpdate constructs a channel point from a lnrpc pending
|
|
|
|
// update.
|
|
|
|
func ChanPointFromPendingUpdate(pu *lnrpc.PendingUpdate) *lnrpc.ChannelPoint {
|
|
|
|
chanPoint := &lnrpc.ChannelPoint{
|
|
|
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
|
|
|
FundingTxidBytes: pu.Txid,
|
|
|
|
},
|
|
|
|
OutputIndex: pu.OutputIndex,
|
|
|
|
}
|
|
|
|
|
|
|
|
return chanPoint
|
|
|
|
}
|
2022-08-03 21:38:09 +02:00
|
|
|
|
|
|
|
// channelPointStr returns the string representation of the channel's
|
|
|
|
// funding transaction.
|
|
|
|
func channelPointStr(chanPoint *lnrpc.ChannelPoint) string {
|
|
|
|
fundingTxID, err := lnrpc.GetChanPointFundingTxid(chanPoint)
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
cp := wire.OutPoint{
|
|
|
|
Hash: *fundingTxID,
|
|
|
|
Index: chanPoint.OutputIndex,
|
|
|
|
}
|
|
|
|
|
|
|
|
return cp.String()
|
|
|
|
}
|
2022-08-12 09:41:00 +02:00
|
|
|
|
2023-08-09 06:03:30 +02:00
|
|
|
// CommitTypeHasTaproot returns whether commitType is a taproot commitment.
|
|
|
|
func CommitTypeHasTaproot(commitType lnrpc.CommitmentType) bool {
|
|
|
|
switch commitType {
|
|
|
|
case lnrpc.CommitmentType_SIMPLE_TAPROOT:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-12 09:41:00 +02:00
|
|
|
// CommitTypeHasAnchors returns whether commitType uses anchor outputs.
|
|
|
|
func CommitTypeHasAnchors(commitType lnrpc.CommitmentType) bool {
|
|
|
|
switch commitType {
|
|
|
|
case lnrpc.CommitmentType_ANCHORS,
|
2023-08-11 06:53:47 +02:00
|
|
|
lnrpc.CommitmentType_SIMPLE_TAPROOT,
|
2022-08-12 09:41:00 +02:00
|
|
|
lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NodeArgsForCommitType returns the command line flag to supply to enable this
|
|
|
|
// commitment type.
|
|
|
|
func NodeArgsForCommitType(commitType lnrpc.CommitmentType) []string {
|
|
|
|
switch commitType {
|
|
|
|
case lnrpc.CommitmentType_LEGACY:
|
|
|
|
return []string{"--protocol.legacy.committweak"}
|
|
|
|
case lnrpc.CommitmentType_STATIC_REMOTE_KEY:
|
|
|
|
return []string{}
|
|
|
|
case lnrpc.CommitmentType_ANCHORS:
|
|
|
|
return []string{"--protocol.anchors"}
|
|
|
|
case lnrpc.CommitmentType_SCRIPT_ENFORCED_LEASE:
|
|
|
|
return []string{
|
|
|
|
"--protocol.anchors",
|
|
|
|
"--protocol.script-enforced-lease",
|
|
|
|
}
|
2023-08-09 06:03:30 +02:00
|
|
|
case lnrpc.CommitmentType_SIMPLE_TAPROOT:
|
|
|
|
return []string{
|
|
|
|
"--protocol.anchors",
|
|
|
|
"--protocol.simple-taproot-chans",
|
|
|
|
}
|
2022-08-12 09:41:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-08-12 10:02:32 +02:00
|
|
|
|
|
|
|
// CalcStaticFee calculates appropriate fees for commitment transactions. This
|
|
|
|
// function provides a simple way to allow test balance assertions to take fee
|
|
|
|
// calculations into account.
|
|
|
|
func CalcStaticFee(c lnrpc.CommitmentType, numHTLCs int) btcutil.Amount {
|
2022-08-12 09:49:54 +02:00
|
|
|
//nolint:lll
|
2022-08-12 10:02:32 +02:00
|
|
|
const (
|
|
|
|
htlcWeight = input.HTLCWeight
|
2022-08-12 09:49:54 +02:00
|
|
|
anchorSize = 330 * 2
|
2022-08-12 10:02:32 +02:00
|
|
|
defaultSatPerVByte = lnwallet.DefaultAnchorsCommitMaxFeeRateSatPerVByte
|
2022-08-12 09:49:54 +02:00
|
|
|
scale = 1000
|
2022-08-12 10:02:32 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
anchors = btcutil.Amount(0)
|
|
|
|
commitWeight = input.CommitWeight
|
|
|
|
feePerKw = chainfee.SatPerKWeight(DefaultFeeRateSatPerKw)
|
|
|
|
)
|
|
|
|
|
2023-08-09 06:03:30 +02:00
|
|
|
switch {
|
|
|
|
// The taproot commitment type has the extra anchor outputs, but also a
|
|
|
|
// smaller witness field (will just be a normal key spend), so we need
|
|
|
|
// to account for that here as well.
|
|
|
|
case CommitTypeHasTaproot(c):
|
|
|
|
feePerKw = chainfee.SatPerKVByte(
|
|
|
|
defaultSatPerVByte * scale,
|
|
|
|
).FeePerKWeight()
|
|
|
|
|
|
|
|
commitWeight = input.TaprootCommitWeight
|
|
|
|
anchors = anchorSize
|
|
|
|
|
2022-08-12 10:02:32 +02:00
|
|
|
// The anchor commitment type is slightly heavier, and we must also add
|
|
|
|
// the value of the two anchors to the resulting fee the initiator
|
|
|
|
// pays. In addition the fee rate is capped at 10 sat/vbyte for anchor
|
|
|
|
// channels.
|
2023-08-09 06:03:30 +02:00
|
|
|
case CommitTypeHasAnchors(c):
|
2022-08-12 10:02:32 +02:00
|
|
|
feePerKw = chainfee.SatPerKVByte(
|
2022-08-12 09:49:54 +02:00
|
|
|
defaultSatPerVByte * scale,
|
2022-08-12 10:02:32 +02:00
|
|
|
).FeePerKWeight()
|
|
|
|
commitWeight = input.AnchorCommitWeight
|
2022-08-12 09:49:54 +02:00
|
|
|
anchors = anchorSize
|
2022-08-12 10:02:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return feePerKw.FeeForWeight(int64(commitWeight+htlcWeight*numHTLCs)) +
|
|
|
|
anchors
|
|
|
|
}
|
|
|
|
|
|
|
|
// CalculateMaxHtlc re-implements the RequiredRemoteChannelReserve of the
|
|
|
|
// funding manager's config, which corresponds to the maximum MaxHTLC value we
|
|
|
|
// allow users to set when updating a channel policy.
|
|
|
|
func CalculateMaxHtlc(chanCap btcutil.Amount) uint64 {
|
2022-08-12 09:49:54 +02:00
|
|
|
const ratio = 100
|
|
|
|
reserve := lnwire.NewMSatFromSatoshis(chanCap / ratio)
|
2022-08-12 10:02:32 +02:00
|
|
|
max := lnwire.NewMSatFromSatoshis(chanCap) - reserve
|
2022-08-12 09:49:54 +02:00
|
|
|
|
2022-08-12 10:02:32 +02:00
|
|
|
return uint64(max)
|
|
|
|
}
|