mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-03 17:26:57 +01:00
multi: replace DefaultDustLimit with script-specific DustLimitForSize
This commit updates call-sites to use the proper dust limits for various script types. This also updates the default dust limit used in the funding flow to be 354 satoshis instead of 573 satoshis.
This commit is contained in:
parent
3385d38414
commit
fdcd726f9a
27 changed files with 330 additions and 154 deletions
|
@ -174,15 +174,6 @@ const (
|
|||
BtcToLtcConversionRate = 60
|
||||
)
|
||||
|
||||
// DefaultBtcChannelConstraints is the default set of channel constraints that are
|
||||
// meant to be used when initially funding a Bitcoin channel.
|
||||
//
|
||||
// TODO(halseth): make configurable at startup?
|
||||
var DefaultBtcChannelConstraints = channeldb.ChannelConstraints{
|
||||
DustLimit: lnwallet.DefaultDustLimit(),
|
||||
MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
|
||||
}
|
||||
|
||||
// DefaultLtcChannelConstraints is the default set of channel constraints that are
|
||||
// meant to be used when initially funding a Litecoin channel.
|
||||
var DefaultLtcChannelConstraints = channeldb.ChannelConstraints{
|
||||
|
@ -235,6 +226,19 @@ type ChainControl struct {
|
|||
MinHtlcIn lnwire.MilliSatoshi
|
||||
}
|
||||
|
||||
// GenDefaultBtcChannelConstraints generates the default set of channel
|
||||
// constraints that are to be used when funding a Bitcoin channel.
|
||||
func GenDefaultBtcConstraints() channeldb.ChannelConstraints {
|
||||
// We use the dust limit for the maximally sized witness program with
|
||||
// a 40-byte data push.
|
||||
dustLimit := lnwallet.DustLimitForSize(input.UnknownWitnessSize)
|
||||
|
||||
return channeldb.ChannelConstraints{
|
||||
DustLimit: dustLimit,
|
||||
MaxAcceptedHtlcs: input.MaxHTLCNumber / 2,
|
||||
}
|
||||
}
|
||||
|
||||
// NewChainControl attempts to create a ChainControl instance according
|
||||
// to the parameters in the passed configuration. Currently three
|
||||
// branches of ChainControl instances exist: one backed by a running btcd
|
||||
|
@ -674,7 +678,7 @@ func NewChainControl(cfg *Config, blockCache *blockcache.BlockCache) (
|
|||
cc.Wc = wc
|
||||
|
||||
// Select the default channel constraints for the primary chain.
|
||||
channelConstraints := DefaultBtcChannelConstraints
|
||||
channelConstraints := GenDefaultBtcConstraints()
|
||||
if cfg.PrimaryChain() == LitecoinChain {
|
||||
channelConstraints = DefaultLtcChannelConstraints
|
||||
}
|
||||
|
|
|
@ -3114,19 +3114,10 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
|||
maxCSV = f.cfg.MaxLocalCSVDelay
|
||||
}
|
||||
|
||||
// We'll determine our dust limit depending on which chain is active.
|
||||
var ourDustLimit btcutil.Amount
|
||||
switch f.cfg.RegisteredChains.PrimaryChain() {
|
||||
case chainreg.BitcoinChain:
|
||||
ourDustLimit = lnwallet.DefaultDustLimit()
|
||||
case chainreg.LitecoinChain:
|
||||
ourDustLimit = chainreg.DefaultLitecoinDustLimit
|
||||
}
|
||||
log.Infof("Initiating fundingRequest(local_amt=%v "+
|
||||
"(subtract_fees=%v), push_amt=%v, chain_hash=%v, peer=%x, "+
|
||||
"dust_limit=%v, min_confs=%v)", localAmt, msg.SubtractFees,
|
||||
msg.PushAmt, msg.ChainHash, peerKey.SerializeCompressed(),
|
||||
ourDustLimit, msg.MinConfs)
|
||||
"min_confs=%v)", localAmt, msg.SubtractFees, msg.PushAmt,
|
||||
msg.ChainHash, peerKey.SerializeCompressed(), msg.MinConfs)
|
||||
|
||||
// We set the channel flags to indicate whether we want this channel to
|
||||
// be announced to the network.
|
||||
|
@ -3300,6 +3291,12 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
|||
// request to the remote peer, kicking off the funding workflow.
|
||||
ourContribution := reservation.OurContribution()
|
||||
|
||||
// Fetch our dust limit which is part of the default channel
|
||||
// constraints, and log it.
|
||||
ourDustLimit := ourContribution.DustLimit
|
||||
|
||||
log.Infof("Dust limit for pendingID(%x): %v", chanID, ourDustLimit)
|
||||
|
||||
// Finally, we'll use the current value of the channels and our default
|
||||
// policy to determine of required commitment constraints for the
|
||||
// remote party.
|
||||
|
@ -3313,7 +3310,7 @@ func (f *Manager) handleInitFundingMsg(msg *InitFundingMsg) {
|
|||
PendingChannelID: chanID,
|
||||
FundingAmount: capacity,
|
||||
PushAmount: msg.PushAmt,
|
||||
DustLimit: ourContribution.DustLimit,
|
||||
DustLimit: ourDustLimit,
|
||||
MaxValueInFlight: maxValue,
|
||||
ChannelReserve: chanReserve,
|
||||
HtlcMinimum: minHtlcIn,
|
||||
|
|
|
@ -276,7 +276,7 @@ func createTestWallet(cdb *channeldb.DB, netParams *chaincfg.Params,
|
|||
ChainIO: bio,
|
||||
FeeEstimator: estimator,
|
||||
NetParams: *netParams,
|
||||
DefaultConstraints: chainreg.DefaultBtcChannelConstraints,
|
||||
DefaultConstraints: chainreg.GenDefaultBtcConstraints(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
11
go.mod
11
go.mod
|
@ -5,13 +5,14 @@ require (
|
|||
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e // indirect
|
||||
github.com/NebulousLabs/go-upnp v0.0.0-20180202185039-29b680b06c82
|
||||
github.com/Yawning/aez v0.0.0-20180114000226-4dad034d9db2
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210803133449-f5a1fb9965e4
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210916191717-f8e6854197cd
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f
|
||||
github.com/btcsuite/btcutil v1.0.3-0.20210527170813-e2ba6805a890
|
||||
github.com/btcsuite/btcutil/psbt v1.0.3-0.20210527170813-e2ba6805a890
|
||||
github.com/btcsuite/btcwallet v0.12.1-0.20210826004415-4ef582f76b02
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.2-0.20210803004036-eebed51155ec
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.1.0
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.1.0
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 // indirect
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec
|
||||
github.com/btcsuite/btcwallet/wtxmgr v1.3.1-0.20210822222949-9b5a201c344c
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
|
||||
|
@ -37,7 +38,7 @@ require (
|
|||
github.com/juju/testing v0.0.0-20190723135506-ce30eb24acd2 // indirect
|
||||
github.com/juju/utils v0.0.0-20180820210520-bf9cc5bdd62d // indirect
|
||||
github.com/juju/version v0.0.0-20180108022336-b64dbd566305 // indirect
|
||||
github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec
|
||||
github.com/kkdai/bstream v1.0.0
|
||||
github.com/lightninglabs/neutrino v0.12.1
|
||||
github.com/lightninglabs/protobuf-hex-display v1.4.3-hex-display
|
||||
github.com/lightningnetwork/lightning-onion v1.0.2-0.20210520211913-522b799e65b1
|
||||
|
@ -56,7 +57,7 @@ require (
|
|||
github.com/urfave/cli v1.20.0
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0
|
||||
go.etcd.io/etcd/client/v3 v3.5.0
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
|
||||
golang.org/x/net v0.0.0-20210913180222-943fd674d43e
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/sys v0.0.0-20210915083310-ed5796bab164 // indirect
|
||||
|
|
19
go.sum
19
go.sum
|
@ -76,8 +76,9 @@ github.com/btcsuite/btcd v0.0.0-20190629003639-c26ffa870fd8/go.mod h1:3J08xEfcug
|
|||
github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.21.0-beta.0.20201208033208-6bd4c64a54fa/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210803133449-f5a1fb9965e4 h1:EmyLrldY44jDVa3dQ2iscj1S6ExuVJhRzCZBOXo93r0=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210803133449-f5a1fb9965e4/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210916191717-f8e6854197cd h1:Nq1fLF6IA8XfW0HTpLaVZoDKazt05J1C2AAeswYloBE=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210916191717-f8e6854197cd/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
|
@ -92,13 +93,15 @@ github.com/btcsuite/btcwallet v0.12.1-0.20210826004415-4ef582f76b02 h1:Q8Scm1SXN
|
|||
github.com/btcsuite/btcwallet v0.12.1-0.20210826004415-4ef582f76b02/go.mod h1:SdqXKJoEEi5LJq6zU67PcKiyqF97AcUOfBfyQHC7rqQ=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.1-0.20210329233242-e0607006dce6/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.2-0.20210803004036-eebed51155ec h1:nuO8goa4gbgDM4iegCztF7mTq8io9NT1DAMoPrEI6S4=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.0.2-0.20210803004036-eebed51155ec/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0 h1:2VsfS0sBedcM5KmDzRMT3+b6xobqWveZGvjb+jFez5w=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.1.0 h1:8pO0pvPX1rFRfRiol4oV6kX7dY5y4chPwhfVwUfvwtk=
|
||||
github.com/btcsuite/btcwallet/wallet/txauthor v1.1.0/go.mod h1:ktYuJyumYtwG+QQ832Q+kqvxWJRAei3Nqs5qhSn4nww=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.0.0/go.mod h1:UwQE78yCerZ313EXZwEiu3jNAtfXj2n2+c8RWiE/WNA=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.1.0 h1:Vg8G8zhNVjaCdwJg2QOmLoWn4RTP7K0J9xlwY8CJnLY=
|
||||
github.com/btcsuite/btcwallet/wallet/txrules v1.1.0/go.mod h1:Zn9UTqpiTH+HOd5BLzSBzULzlOPmcoeyQIA0cp0WbQQ=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210519225359-6ab9b615576f h1:bzrmHuQ3ZGWWhGDyTL0OqihQWXGXSXNuBPkDoDB8SS4=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.0.1-0.20210519225359-6ab9b615576f/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0 h1:wZnOolEAeNOHzHTnznw/wQv+j35ftCIokNrnOTOU5o8=
|
||||
github.com/btcsuite/btcwallet/wallet/txsizes v1.1.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.4/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU=
|
||||
github.com/btcsuite/btcwallet/walletdb v1.3.6-0.20210803004036-eebed51155ec h1:zcAU3Ij8SmqaE+ITtS76fua2Niq7DRNp46sJRhi8PiI=
|
||||
|
@ -408,8 +411,9 @@ github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQL
|
|||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec h1:n1NeQ3SgUHyISrjFFoO5dR748Is8dBL9qpaTNfphQrs=
|
||||
github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8=
|
||||
github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I=
|
||||
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
|
@ -674,8 +678,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
|
@ -45,6 +45,55 @@ func WitnessScriptHash(witnessScript []byte) ([]byte, error) {
|
|||
return bldr.Script()
|
||||
}
|
||||
|
||||
// WitnessPubKeyHash generates a pay-to-witness-pubkey-hash public key script
|
||||
// paying to a version 0 witness program containing the passed serialized
|
||||
// public key.
|
||||
func WitnessPubKeyHash(pubkey []byte) ([]byte, error) {
|
||||
bldr := txscript.NewScriptBuilder()
|
||||
|
||||
bldr.AddOp(txscript.OP_0)
|
||||
pkhash := btcutil.Hash160(pubkey)
|
||||
bldr.AddData(pkhash)
|
||||
return bldr.Script()
|
||||
}
|
||||
|
||||
// GenerateP2SH generates a pay-to-script-hash public key script paying to the
|
||||
// passed redeem script.
|
||||
func GenerateP2SH(script []byte) ([]byte, error) {
|
||||
bldr := txscript.NewScriptBuilder()
|
||||
|
||||
bldr.AddOp(txscript.OP_HASH160)
|
||||
scripthash := btcutil.Hash160(script)
|
||||
bldr.AddData(scripthash)
|
||||
bldr.AddOp(txscript.OP_EQUAL)
|
||||
return bldr.Script()
|
||||
}
|
||||
|
||||
// GenerateP2PKH generates a pay-to-public-key-hash public key script paying to
|
||||
// the passed serialized public key.
|
||||
func GenerateP2PKH(pubkey []byte) ([]byte, error) {
|
||||
bldr := txscript.NewScriptBuilder()
|
||||
|
||||
bldr.AddOp(txscript.OP_DUP)
|
||||
bldr.AddOp(txscript.OP_HASH160)
|
||||
pkhash := btcutil.Hash160(pubkey)
|
||||
bldr.AddData(pkhash)
|
||||
bldr.AddOp(txscript.OP_EQUALVERIFY)
|
||||
bldr.AddOp(txscript.OP_CHECKSIG)
|
||||
return bldr.Script()
|
||||
}
|
||||
|
||||
// GenerateUnknownWitness generates the maximum-sized witness public key script
|
||||
// consisting of a version push and a 40-byte data push.
|
||||
func GenerateUnknownWitness() ([]byte, error) {
|
||||
bldr := txscript.NewScriptBuilder()
|
||||
|
||||
bldr.AddOp(txscript.OP_0)
|
||||
witnessScript := make([]byte, 40)
|
||||
bldr.AddData(witnessScript)
|
||||
return bldr.Script()
|
||||
}
|
||||
|
||||
// GenMultiSigScript generates the non-p2sh'd multisig script for 2 of 2
|
||||
// pubkeys.
|
||||
func GenMultiSigScript(aPub, bPub []byte) ([]byte, error) {
|
||||
|
|
|
@ -41,11 +41,20 @@ const (
|
|||
// - P2WSHWitnessProgram: 34 bytes
|
||||
NestedP2WSHSize = 1 + P2WSHSize
|
||||
|
||||
// UnknownWitnessSize 42 bytes
|
||||
// - OP_x: 1 byte
|
||||
// - OP_DATA: 1 byte (max-size length)
|
||||
// - max-size: 40 bytes
|
||||
UnknownWitnessSize = 1 + 1 + 40
|
||||
|
||||
// P2PKHSize 25 bytes
|
||||
P2PKHSize = 25
|
||||
|
||||
// P2PKHOutputSize 34 bytes
|
||||
// - value: 8 bytes
|
||||
// - var_int: 1 byte (pkscript_length)
|
||||
// - pkscript (p2pkh): 25 bytes
|
||||
P2PKHOutputSize = 8 + 1 + 25
|
||||
P2PKHOutputSize = 8 + 1 + P2PKHSize
|
||||
|
||||
// P2WKHOutputSize 31 bytes
|
||||
// - value: 8 bytes
|
||||
|
@ -59,11 +68,14 @@ const (
|
|||
// - pkscript (p2wsh): 34 bytes
|
||||
P2WSHOutputSize = 8 + 1 + P2WSHSize
|
||||
|
||||
// P2SHSize 23 bytes
|
||||
P2SHSize = 23
|
||||
|
||||
// P2SHOutputSize 32 bytes
|
||||
// - value: 8 bytes
|
||||
// - var_int: 1 byte (pkscript_length)
|
||||
// - pkscript (p2sh): 23 bytes
|
||||
P2SHOutputSize = 8 + 1 + 23
|
||||
P2SHOutputSize = 8 + 1 + P2SHSize
|
||||
|
||||
// P2PKHScriptSigSize 108 bytes
|
||||
// - OP_DATA: 1 byte (signature length)
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/lightningnetwork/lnd/chainreg"
|
||||
"github.com/lightningnetwork/lnd/funding"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lncfg"
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
|
@ -417,12 +418,15 @@ func testListChannels(net *lntest.NetworkHarness, t *harnessTest) {
|
|||
// Check the returned response is correct.
|
||||
aliceChannel := resp.Channels[0]
|
||||
|
||||
// Calculate the dust limit we'll use for the test.
|
||||
dustLimit := lnwallet.DustLimitForSize(input.UnknownWitnessSize)
|
||||
|
||||
// defaultConstraints is a ChannelConstraints with default values. It is
|
||||
// used to test against Alice's local channel constraints.
|
||||
defaultConstraints := &lnrpc.ChannelConstraints{
|
||||
CsvDelay: 4,
|
||||
ChanReserveSat: 1000,
|
||||
DustLimitSat: uint64(lnwallet.DefaultDustLimit()),
|
||||
DustLimitSat: uint64(dustLimit),
|
||||
MaxPendingAmtMsat: 99000000,
|
||||
MinHtlcMsat: 1,
|
||||
MaxAcceptedHtlcs: bobRemoteMaxHtlcs,
|
||||
|
@ -438,7 +442,7 @@ func testListChannels(net *lntest.NetworkHarness, t *harnessTest) {
|
|||
customizedConstraints := &lnrpc.ChannelConstraints{
|
||||
CsvDelay: 4,
|
||||
ChanReserveSat: 1000,
|
||||
DustLimitSat: uint64(lnwallet.DefaultDustLimit()),
|
||||
DustLimitSat: uint64(dustLimit),
|
||||
MaxPendingAmtMsat: 99000000,
|
||||
MinHtlcMsat: customizedMinHtlc,
|
||||
MaxAcceptedHtlcs: aliceRemoteMaxHtlcs,
|
||||
|
|
|
@ -219,7 +219,7 @@ func CoinSelectSubtractFees(feeRate chainfee.SatPerKWeight, amt,
|
|||
|
||||
// If the the output is too small after subtracting the fee, the coin
|
||||
// selection cannot be performed with an amount this small.
|
||||
if outputAmt <= dustLimit {
|
||||
if outputAmt < dustLimit {
|
||||
return nil, 0, 0, fmt.Errorf("output amount(%v) after "+
|
||||
"subtracting fees(%v) below dust limit(%v)", outputAmt,
|
||||
requiredFeeNoChange, dustLimit)
|
||||
|
@ -233,7 +233,7 @@ func CoinSelectSubtractFees(feeRate chainfee.SatPerKWeight, amt,
|
|||
// If adding a change output leads to both outputs being above
|
||||
// the dust limit, we'll add the change output. Otherwise we'll
|
||||
// go with the no change tx we originally found.
|
||||
if newChange > dustLimit && newOutput > dustLimit {
|
||||
if newChange >= dustLimit && newOutput >= dustLimit {
|
||||
outputAmt = newOutput
|
||||
changeAmt = newChange
|
||||
}
|
||||
|
|
|
@ -383,7 +383,7 @@ func TestCoinSelectSubtractFees(t *testing.T) {
|
|||
{
|
||||
TxOut: wire.TxOut{
|
||||
PkScript: p2wkhScript,
|
||||
Value: int64(fundingFee(feeRate, 1, false) + dustLimit),
|
||||
Value: int64(fundingFee(feeRate, 1, false) + dustLimit - 1),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -304,7 +304,7 @@ func (w *WalletAssembler) ProvisionChannel(r *Request) (Intent, error) {
|
|||
|
||||
// Sanity check: The addition of the outputs should not lead to the
|
||||
// creation of dust.
|
||||
if changeAmt != 0 && changeAmt <= w.cfg.DustLimit {
|
||||
if changeAmt != 0 && changeAmt < w.cfg.DustLimit {
|
||||
return fmt.Errorf("change amount(%v) after coin "+
|
||||
"select is below dust limit(%v)", changeAmt,
|
||||
w.cfg.DustLimit)
|
||||
|
|
|
@ -1,13 +1,50 @@
|
|||
package lnwallet
|
||||
|
||||
import (
|
||||
"github.com/btcsuite/btcd/mempool"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/wallet/txrules"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
)
|
||||
|
||||
// DefaultDustLimit is used to calculate the dust HTLC amount which will be
|
||||
// send to other node during funding process.
|
||||
func DefaultDustLimit() btcutil.Amount {
|
||||
return txrules.GetDustThreshold(input.P2WSHSize, txrules.DefaultRelayFeePerKb)
|
||||
// DustLimitForSize retrieves the dust limit for a given pkscript size. Given
|
||||
// the size, it automatically determines whether the script is a witness script
|
||||
// or not. It calls btcd's GetDustThreshold method under the hood. It must be
|
||||
// called with a proper size parameter or else a panic occurs.
|
||||
func DustLimitForSize(scriptSize int) btcutil.Amount {
|
||||
var (
|
||||
dustlimit btcutil.Amount
|
||||
pkscript []byte
|
||||
)
|
||||
|
||||
// With the size of the script, determine which type of pkscript to
|
||||
// create. This will be used in the call to GetDustThreshold. We pass
|
||||
// in an empty byte slice since the contents of the script itself don't
|
||||
// matter.
|
||||
switch scriptSize {
|
||||
case input.P2WPKHSize:
|
||||
pkscript, _ = input.WitnessPubKeyHash([]byte{})
|
||||
|
||||
case input.P2WSHSize:
|
||||
pkscript, _ = input.WitnessScriptHash([]byte{})
|
||||
|
||||
case input.P2SHSize:
|
||||
pkscript, _ = input.GenerateP2SH([]byte{})
|
||||
|
||||
case input.P2PKHSize:
|
||||
pkscript, _ = input.GenerateP2PKH([]byte{})
|
||||
|
||||
case input.UnknownWitnessSize:
|
||||
pkscript, _ = input.GenerateUnknownWitness()
|
||||
|
||||
default:
|
||||
panic("invalid script size")
|
||||
}
|
||||
|
||||
// Call GetDustThreshold with a TxOut containing the generated
|
||||
// pkscript.
|
||||
txout := &wire.TxOut{PkScript: pkscript}
|
||||
dustlimit = btcutil.Amount(mempool.GetDustThreshold(txout))
|
||||
|
||||
return dustlimit
|
||||
}
|
||||
|
|
56
lnwallet/parameters_test.go
Normal file
56
lnwallet/parameters_test.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package lnwallet
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestDustLimitForSize tests that we receive the expected dust limits for
|
||||
// various script types from btcd's GetDustThreshold function.
|
||||
func TestDustLimitForSize(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
size int
|
||||
expectedLimit btcutil.Amount
|
||||
}{
|
||||
{
|
||||
name: "p2pkh dust limit",
|
||||
size: input.P2PKHSize,
|
||||
expectedLimit: btcutil.Amount(546),
|
||||
},
|
||||
{
|
||||
name: "p2sh dust limit",
|
||||
size: input.P2SHSize,
|
||||
expectedLimit: btcutil.Amount(540),
|
||||
},
|
||||
{
|
||||
name: "p2wpkh dust limit",
|
||||
size: input.P2WPKHSize,
|
||||
expectedLimit: btcutil.Amount(294),
|
||||
},
|
||||
{
|
||||
name: "p2wsh dust limit",
|
||||
size: input.P2WSHSize,
|
||||
expectedLimit: btcutil.Amount(330),
|
||||
},
|
||||
{
|
||||
name: "unknown witness limit",
|
||||
size: input.UnknownWitnessSize,
|
||||
expectedLimit: btcutil.Amount(354),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
dustlimit := DustLimitForSize(test.size)
|
||||
require.Equal(t, test.expectedLimit, dustlimit)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -205,6 +205,9 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
|
|||
feeMSat += 2 * lnwire.NewMSatFromSatoshis(anchorSize)
|
||||
}
|
||||
|
||||
// Used to cut down on verbosity.
|
||||
defaultDust := wallet.Cfg.DefaultConstraints.DustLimit
|
||||
|
||||
// If we're the responder to a single-funder reservation, then we have
|
||||
// no initial balance in the channel unless the remote party is pushing
|
||||
// some funds to us within the first commitment state.
|
||||
|
@ -218,7 +221,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
|
|||
if int64(theirBalance) < 0 {
|
||||
return nil, ErrFunderBalanceDust(
|
||||
int64(commitFee), int64(theirBalance.ToSatoshis()),
|
||||
int64(2*DefaultDustLimit()),
|
||||
int64(2*defaultDust),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
@ -247,7 +250,7 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
|
|||
if int64(ourBalance) < 0 {
|
||||
return nil, ErrFunderBalanceDust(
|
||||
int64(commitFee), int64(ourBalance),
|
||||
int64(2*DefaultDustLimit()),
|
||||
int64(2*defaultDust),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -257,21 +260,21 @@ func NewChannelReservation(capacity, localFundingAmt btcutil.Amount,
|
|||
// reject this channel creation request.
|
||||
//
|
||||
// TODO(roasbeef): reject if 30% goes to fees? dust channel
|
||||
if initiator && ourBalance.ToSatoshis() <= 2*DefaultDustLimit() {
|
||||
if initiator && ourBalance.ToSatoshis() <= 2*defaultDust {
|
||||
return nil, ErrFunderBalanceDust(
|
||||
int64(commitFee),
|
||||
int64(ourBalance.ToSatoshis()),
|
||||
int64(2*DefaultDustLimit()),
|
||||
int64(2*defaultDust),
|
||||
)
|
||||
}
|
||||
|
||||
// Similarly we ensure their balance is reasonable if we are not the
|
||||
// initiator.
|
||||
if !initiator && theirBalance.ToSatoshis() <= 2*DefaultDustLimit() {
|
||||
if !initiator && theirBalance.ToSatoshis() <= 2*defaultDust {
|
||||
return nil, ErrFunderBalanceDust(
|
||||
int64(commitFee),
|
||||
int64(theirBalance.ToSatoshis()),
|
||||
int64(2*DefaultDustLimit()),
|
||||
int64(2*defaultDust),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -446,7 +446,7 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness,
|
|||
}
|
||||
aliceChanReservation.SetNumConfsRequired(numReqConfs)
|
||||
channelConstraints := &channeldb.ChannelConstraints{
|
||||
DustLimit: lnwallet.DefaultDustLimit(),
|
||||
DustLimit: alice.Cfg.DefaultConstraints.DustLimit,
|
||||
ChanReserve: fundingAmount / 100,
|
||||
MaxPendingAmount: lnwire.NewMSatFromSatoshis(fundingAmount),
|
||||
MinHTLC: 1,
|
||||
|
@ -896,7 +896,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
|
|||
}
|
||||
aliceChanReservation.SetNumConfsRequired(numReqConfs)
|
||||
channelConstraints := &channeldb.ChannelConstraints{
|
||||
DustLimit: lnwallet.DefaultDustLimit(),
|
||||
DustLimit: alice.Cfg.DefaultConstraints.DustLimit,
|
||||
ChanReserve: fundingAmt / 100,
|
||||
MaxPendingAmount: lnwire.NewMSatFromSatoshis(fundingAmt),
|
||||
MinHTLC: 1,
|
||||
|
|
|
@ -542,16 +542,19 @@ func testSpendValidation(t *testing.T, tweakless bool) {
|
|||
Privkeys: []*btcec.PrivateKey{aliceKeyPriv},
|
||||
}
|
||||
|
||||
// Calculate the dust limit we'll use for the test.
|
||||
dustLimit := DustLimitForSize(input.UnknownWitnessSize)
|
||||
|
||||
aliceChanCfg := &channeldb.ChannelConfig{
|
||||
ChannelConstraints: channeldb.ChannelConstraints{
|
||||
DustLimit: DefaultDustLimit(),
|
||||
DustLimit: dustLimit,
|
||||
CsvDelay: csvTimeout,
|
||||
},
|
||||
}
|
||||
|
||||
bobChanCfg := &channeldb.ChannelConfig{
|
||||
ChannelConstraints: channeldb.ChannelConstraints{
|
||||
DustLimit: DefaultDustLimit(),
|
||||
DustLimit: dustLimit,
|
||||
CsvDelay: csvTimeout,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -689,12 +689,15 @@ func (l *LightningWallet) handleFundingReserveRequest(req *InitFundingReserveMsg
|
|||
// If no chanFunder was provided, then we'll assume the default
|
||||
// assembler, which is backed by the wallet's internal coin selection.
|
||||
if req.ChanFunder == nil {
|
||||
// We use the P2WSH dust limit since it is larger than the
|
||||
// P2WPKH dust limit and to avoid threading through two
|
||||
// different dust limits.
|
||||
cfg := chanfunding.WalletConfig{
|
||||
CoinSource: &CoinSource{l},
|
||||
CoinSelectLocker: l,
|
||||
CoinLocker: l,
|
||||
Signer: l.Cfg.Signer,
|
||||
DustLimit: DefaultDustLimit(),
|
||||
DustLimit: DustLimitForSize(input.P2WSHSize),
|
||||
}
|
||||
req.ChanFunder = chanfunding.NewWalletAssembler(cfg)
|
||||
}
|
||||
|
|
|
@ -1227,8 +1227,7 @@ func (r *rpcServer) SendCoins(ctx context.Context,
|
|||
// pay to the change address created above if we needed to
|
||||
// reserve any value, the rest will go to targetAddr.
|
||||
sweepTxPkg, err := sweep.CraftSweepAllTx(
|
||||
feePerKw, lnwallet.DefaultDustLimit(),
|
||||
uint32(bestHeight), nil, targetAddr, wallet,
|
||||
feePerKw, uint32(bestHeight), nil, targetAddr, wallet,
|
||||
wallet, wallet.WalletController,
|
||||
r.server.cc.FeeEstimator, r.server.cc.Signer,
|
||||
minConfs,
|
||||
|
@ -1280,9 +1279,9 @@ func (r *rpcServer) SendCoins(ctx context.Context,
|
|||
}
|
||||
|
||||
sweepTxPkg, err = sweep.CraftSweepAllTx(
|
||||
feePerKw, lnwallet.DefaultDustLimit(),
|
||||
uint32(bestHeight), outputs, targetAddr, wallet,
|
||||
wallet, wallet.WalletController,
|
||||
feePerKw, uint32(bestHeight), outputs,
|
||||
targetAddr, wallet, wallet,
|
||||
wallet.WalletController,
|
||||
r.server.cc.FeeEstimator, r.server.cc.Signer,
|
||||
minConfs,
|
||||
)
|
||||
|
|
|
@ -1150,7 +1150,7 @@ func (s *UtxoSweeper) getInputLists(cluster inputCluster,
|
|||
if len(retryInputs) > 0 {
|
||||
var err error
|
||||
allSets, err = generateInputPartitionings(
|
||||
append(retryInputs, newInputs...), s.relayFeeRate,
|
||||
append(retryInputs, newInputs...),
|
||||
cluster.sweepFeeRate, s.cfg.MaxInputsPerTx,
|
||||
s.cfg.Wallet,
|
||||
)
|
||||
|
@ -1161,8 +1161,8 @@ func (s *UtxoSweeper) getInputLists(cluster inputCluster,
|
|||
|
||||
// Create sets for just the new inputs.
|
||||
newSets, err := generateInputPartitionings(
|
||||
newInputs, s.relayFeeRate, cluster.sweepFeeRate,
|
||||
s.cfg.MaxInputsPerTx, s.cfg.Wallet,
|
||||
newInputs, cluster.sweepFeeRate, s.cfg.MaxInputsPerTx,
|
||||
s.cfg.Wallet,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("input partitionings: %v", err)
|
||||
|
@ -1193,7 +1193,7 @@ func (s *UtxoSweeper) sweep(inputs inputSet, feeRate chainfee.SatPerKWeight,
|
|||
// Create sweep tx.
|
||||
tx, err := createSweepTx(
|
||||
inputs, nil, s.currentOutputScript, uint32(currentHeight),
|
||||
feeRate, dustLimit(s.relayFeeRate), s.cfg.Signer,
|
||||
feeRate, s.cfg.Signer,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create sweep tx: %v", err)
|
||||
|
@ -1488,7 +1488,7 @@ func (s *UtxoSweeper) CreateSweepTx(inputs []input.Input, feePref FeePreference,
|
|||
|
||||
return createSweepTx(
|
||||
inputs, nil, pkScript, currentBlockHeight, feePerKw,
|
||||
dustLimit(s.relayFeeRate), s.cfg.Signer,
|
||||
s.cfg.Signer,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,8 @@ func createSweeperTestContext(t *testing.T) *sweeperTestContext {
|
|||
Store: store,
|
||||
Signer: &mock.DummySigner{},
|
||||
GenSweepScript: func() ([]byte, error) {
|
||||
script := []byte{outputScriptCount}
|
||||
script := make([]byte, input.P2WPKHSize)
|
||||
script[0] = outputScriptCount
|
||||
outputScriptCount++
|
||||
return script, nil
|
||||
},
|
||||
|
@ -1801,6 +1802,24 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
locktime2 := uint32(52)
|
||||
locktime3 := uint32(53)
|
||||
|
||||
aPkScript := make([]byte, input.P2WPKHSize)
|
||||
aPkScript[0] = 'a'
|
||||
|
||||
bPkScript := make([]byte, input.P2WSHSize)
|
||||
bPkScript[0] = 'b'
|
||||
|
||||
cPkScript := make([]byte, input.P2PKHSize)
|
||||
cPkScript[0] = 'c'
|
||||
|
||||
dPkScript := make([]byte, input.P2SHSize)
|
||||
dPkScript[0] = 'd'
|
||||
|
||||
ePkScript := make([]byte, input.UnknownWitnessSize)
|
||||
ePkScript[0] = 'e'
|
||||
|
||||
fPkScript := make([]byte, input.P2WSHSize)
|
||||
fPkScript[0] = 'f'
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
inputs []*testInput
|
||||
|
@ -1815,7 +1834,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
{
|
||||
BaseInput: inputs[0],
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("aaa"),
|
||||
PkScript: aPkScript,
|
||||
Value: 100000,
|
||||
},
|
||||
},
|
||||
|
@ -1836,7 +1855,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
// output must be the first one.
|
||||
require.Equal(t, 2, len(tx.TxOut))
|
||||
out := tx.TxOut[0]
|
||||
require.Equal(t, []byte("aaa"), out.PkScript)
|
||||
require.Equal(t, aPkScript, out.PkScript)
|
||||
require.Equal(t, int64(100000), out.Value)
|
||||
},
|
||||
},
|
||||
|
@ -1848,13 +1867,13 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
{
|
||||
BaseInput: inputs[0],
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("aaa"),
|
||||
PkScript: aPkScript,
|
||||
|
||||
// Fee will be about 5340 sats.
|
||||
// Subtract a bit more to
|
||||
// ensure no dust change output
|
||||
// is manifested.
|
||||
Value: inputs[0].SignDesc().Output.Value - 5600,
|
||||
Value: inputs[0].SignDesc().Output.Value - 6300,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1871,10 +1890,10 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
|
||||
require.Equal(t, 1, len(tx.TxOut))
|
||||
out := tx.TxOut[0]
|
||||
require.Equal(t, []byte("aaa"), out.PkScript)
|
||||
require.Equal(t, aPkScript, out.PkScript)
|
||||
require.Equal(
|
||||
t,
|
||||
inputs[0].SignDesc().Output.Value-5600,
|
||||
inputs[0].SignDesc().Output.Value-6300,
|
||||
out.Value,
|
||||
)
|
||||
},
|
||||
|
@ -1897,7 +1916,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
// The second input requires a TxOut.
|
||||
BaseInput: inputs[0],
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("aaa"),
|
||||
PkScript: aPkScript,
|
||||
Value: inputs[0].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
@ -1916,7 +1935,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
|
||||
// The required TxOut should be the first one.
|
||||
out := tx.TxOut[0]
|
||||
require.Equal(t, []byte("aaa"), out.PkScript)
|
||||
require.Equal(t, aPkScript, out.PkScript)
|
||||
require.Equal(
|
||||
t, inputs[0].SignDesc().Output.Value,
|
||||
out.Value,
|
||||
|
@ -1947,7 +1966,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
{
|
||||
BaseInput: inputs[0],
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("aaa"),
|
||||
PkScript: aPkScript,
|
||||
Value: inputs[0].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
@ -1965,7 +1984,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
|
||||
require.Equal(t, 2, len(tx.TxOut))
|
||||
out := tx.TxOut[0]
|
||||
require.Equal(t, []byte("aaa"), out.PkScript)
|
||||
require.Equal(t, aPkScript, out.PkScript)
|
||||
require.Equal(
|
||||
t, inputs[0].SignDesc().Output.Value,
|
||||
out.Value,
|
||||
|
@ -1980,21 +1999,21 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
{
|
||||
BaseInput: inputs[0],
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("aaa"),
|
||||
PkScript: aPkScript,
|
||||
Value: inputs[0].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
{
|
||||
BaseInput: inputs[1],
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("bbb"),
|
||||
PkScript: bPkScript,
|
||||
Value: inputs[1].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
{
|
||||
BaseInput: inputs[2],
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("ccc"),
|
||||
PkScript: cPkScript,
|
||||
Value: inputs[2].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
@ -2041,7 +2060,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
BaseInput: inputs[0],
|
||||
locktime: &locktime1,
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("aaa"),
|
||||
PkScript: aPkScript,
|
||||
Value: inputs[0].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
@ -2049,7 +2068,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
BaseInput: inputs[1],
|
||||
locktime: &locktime1,
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("bbb"),
|
||||
PkScript: bPkScript,
|
||||
Value: inputs[1].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
@ -2057,7 +2076,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
BaseInput: inputs[2],
|
||||
locktime: &locktime2,
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("ccc"),
|
||||
PkScript: cPkScript,
|
||||
Value: inputs[2].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
@ -2065,7 +2084,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
BaseInput: inputs[3],
|
||||
locktime: &locktime2,
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("ddd"),
|
||||
PkScript: dPkScript,
|
||||
Value: inputs[3].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
@ -2073,7 +2092,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
BaseInput: inputs[4],
|
||||
locktime: &locktime3,
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("eee"),
|
||||
PkScript: ePkScript,
|
||||
Value: inputs[4].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
@ -2081,7 +2100,7 @@ func TestRequiredTxOuts(t *testing.T) {
|
|||
BaseInput: inputs[5],
|
||||
locktime: &locktime3,
|
||||
reqTxOut: &wire.TxOut{
|
||||
PkScript: []byte("fff"),
|
||||
PkScript: fPkScript,
|
||||
Value: inputs[5].SignDesc().Output.Value,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/btcsuite/btcd/txscript"
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/btcsuite/btcwallet/wallet/txrules"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
|
@ -109,9 +108,6 @@ func (t *txInputSetState) clone() txInputSetState {
|
|||
type txInputSet struct {
|
||||
txInputSetState
|
||||
|
||||
// dustLimit is the minimum output value of the tx.
|
||||
dustLimit btcutil.Amount
|
||||
|
||||
// maxInputs is the maximum number of inputs that will be accepted in
|
||||
// the set.
|
||||
maxInputs int
|
||||
|
@ -121,25 +117,15 @@ type txInputSet struct {
|
|||
wallet Wallet
|
||||
}
|
||||
|
||||
func dustLimit(relayFee chainfee.SatPerKWeight) btcutil.Amount {
|
||||
return txrules.GetDustThreshold(
|
||||
input.P2WPKHSize,
|
||||
btcutil.Amount(relayFee.FeePerKVByte()),
|
||||
)
|
||||
}
|
||||
|
||||
// newTxInputSet constructs a new, empty input set.
|
||||
func newTxInputSet(wallet Wallet, feePerKW,
|
||||
relayFee chainfee.SatPerKWeight, maxInputs int) *txInputSet {
|
||||
|
||||
dustLimit := dustLimit(relayFee)
|
||||
func newTxInputSet(wallet Wallet, feePerKW chainfee.SatPerKWeight,
|
||||
maxInputs int) *txInputSet {
|
||||
|
||||
state := txInputSetState{
|
||||
feeRate: feePerKW,
|
||||
}
|
||||
|
||||
b := txInputSet{
|
||||
dustLimit: dustLimit,
|
||||
maxInputs: maxInputs,
|
||||
wallet: wallet,
|
||||
txInputSetState: state,
|
||||
|
@ -153,7 +139,7 @@ func newTxInputSet(wallet Wallet, feePerKW,
|
|||
func (t *txInputSet) enoughInput() bool {
|
||||
// If we have a change output above dust, then we certainly have enough
|
||||
// inputs to the transaction.
|
||||
if t.changeOutput >= t.dustLimit {
|
||||
if t.changeOutput >= lnwallet.DustLimitForSize(input.P2WPKHSize) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -192,8 +178,12 @@ func (t *txInputSet) addToState(inp input.Input, constraints addConstraints) *tx
|
|||
// If the input comes with a required tx out that is below dust, we
|
||||
// won't add it.
|
||||
reqOut := inp.RequiredTxOut()
|
||||
if reqOut != nil && btcutil.Amount(reqOut.Value) < t.dustLimit {
|
||||
return nil
|
||||
if reqOut != nil {
|
||||
// Fetch the dust limit for this output.
|
||||
dustLimit := lnwallet.DustLimitForSize(len(reqOut.PkScript))
|
||||
if btcutil.Amount(reqOut.Value) < dustLimit {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Clone the current set state.
|
||||
|
|
|
@ -14,14 +14,9 @@ import (
|
|||
func TestTxInputSet(t *testing.T) {
|
||||
const (
|
||||
feeRate = 1000
|
||||
relayFee = 300
|
||||
maxInputs = 10
|
||||
)
|
||||
set := newTxInputSet(nil, feeRate, relayFee, maxInputs)
|
||||
|
||||
if set.dustLimit != 537 {
|
||||
t.Fatalf("incorrect dust limit")
|
||||
}
|
||||
set := newTxInputSet(nil, feeRate, maxInputs)
|
||||
|
||||
// Create a 300 sat input. The fee to sweep this input to a P2WKH output
|
||||
// is 439 sats. That means that this input yields -139 sats and we
|
||||
|
@ -66,16 +61,15 @@ func TestTxInputSet(t *testing.T) {
|
|||
func TestTxInputSetFromWallet(t *testing.T) {
|
||||
const (
|
||||
feeRate = 500
|
||||
relayFee = 300
|
||||
maxInputs = 10
|
||||
)
|
||||
|
||||
wallet := &mockWallet{}
|
||||
set := newTxInputSet(wallet, feeRate, relayFee, maxInputs)
|
||||
set := newTxInputSet(wallet, feeRate, maxInputs)
|
||||
|
||||
// Add a 700 sat input to the set. It yields positively, but doesn't
|
||||
// Add a 500 sat input to the set. It yields positively, but doesn't
|
||||
// reach the output dust limit.
|
||||
if !set.add(createP2WKHInput(700), constraintsRegular) {
|
||||
if !set.add(createP2WKHInput(500), constraintsRegular) {
|
||||
t.Fatal("expected add of positively yielding input to succeed")
|
||||
}
|
||||
if set.enoughInput() {
|
||||
|
@ -138,13 +132,9 @@ func (r *reqInput) RequiredTxOut() *wire.TxOut {
|
|||
func TestTxInputSetRequiredOutput(t *testing.T) {
|
||||
const (
|
||||
feeRate = 1000
|
||||
relayFee = 300
|
||||
maxInputs = 10
|
||||
)
|
||||
set := newTxInputSet(nil, feeRate, relayFee, maxInputs)
|
||||
if set.dustLimit != 537 {
|
||||
t.Fatalf("incorrect dust limit")
|
||||
}
|
||||
set := newTxInputSet(nil, feeRate, maxInputs)
|
||||
|
||||
// Attempt to add an input with a required txout below the dust limit.
|
||||
// This should fail since we cannot trim such outputs.
|
||||
|
@ -152,7 +142,7 @@ func TestTxInputSetRequiredOutput(t *testing.T) {
|
|||
Input: createP2WKHInput(500),
|
||||
txOut: &wire.TxOut{
|
||||
Value: 500,
|
||||
PkScript: make([]byte, 33),
|
||||
PkScript: make([]byte, input.P2PKHSize),
|
||||
},
|
||||
}
|
||||
require.False(t, set.add(inp, constraintsRegular),
|
||||
|
@ -164,7 +154,7 @@ func TestTxInputSetRequiredOutput(t *testing.T) {
|
|||
Input: createP2WKHInput(1000),
|
||||
txOut: &wire.TxOut{
|
||||
Value: 1000,
|
||||
PkScript: make([]byte, 22),
|
||||
PkScript: make([]byte, input.P2WPKHSize),
|
||||
},
|
||||
}
|
||||
require.True(t, set.add(inp, constraintsRegular), "failed adding input")
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
)
|
||||
|
||||
|
@ -37,8 +38,8 @@ type inputSet []input.Input
|
|||
// inputs are skipped. No input sets with a total value after fees below the
|
||||
// dust limit are returned.
|
||||
func generateInputPartitionings(sweepableInputs []txInput,
|
||||
relayFeePerKW, feePerKW chainfee.SatPerKWeight,
|
||||
maxInputsPerTx int, wallet Wallet) ([]inputSet, error) {
|
||||
feePerKW chainfee.SatPerKWeight, maxInputsPerTx int,
|
||||
wallet Wallet) ([]inputSet, error) {
|
||||
|
||||
// Sort input by yield. We will start constructing input sets starting
|
||||
// with the highest yield inputs. This is to prevent the construction
|
||||
|
@ -85,9 +86,7 @@ func generateInputPartitionings(sweepableInputs []txInput,
|
|||
// Start building a set of positive-yield tx inputs under the
|
||||
// condition that the tx will be published with the specified
|
||||
// fee rate.
|
||||
txInputs := newTxInputSet(
|
||||
wallet, feePerKW, relayFeePerKW, maxInputsPerTx,
|
||||
)
|
||||
txInputs := newTxInputSet(wallet, feePerKW, maxInputsPerTx)
|
||||
|
||||
// From the set of sweepable inputs, keep adding inputs to the
|
||||
// input set until the tx output value no longer goes up or the
|
||||
|
@ -111,10 +110,12 @@ func generateInputPartitionings(sweepableInputs []txInput,
|
|||
// continuing with the remaining inputs will only lead to sets
|
||||
// with an even lower output value.
|
||||
if !txInputs.enoughInput() {
|
||||
// The change output is always a p2wpkh here.
|
||||
dl := lnwallet.DustLimitForSize(input.P2WPKHSize)
|
||||
log.Debugf("Set value %v (r=%v, c=%v) below dust "+
|
||||
"limit of %v", txInputs.totalOutput(),
|
||||
txInputs.requiredOutput, txInputs.changeOutput,
|
||||
txInputs.dustLimit)
|
||||
dl)
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
|
@ -135,8 +136,8 @@ func generateInputPartitionings(sweepableInputs []txInput,
|
|||
// sending any leftover change to the change script.
|
||||
func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
|
||||
changePkScript []byte, currentBlockHeight uint32,
|
||||
feePerKw chainfee.SatPerKWeight, dustLimit btcutil.Amount,
|
||||
signer input.Signer) (*wire.MsgTx, error) {
|
||||
feePerKw chainfee.SatPerKWeight, signer input.Signer) (*wire.MsgTx,
|
||||
error) {
|
||||
|
||||
inputs, estimator := getWeightEstimate(inputs, outputs, feePerKw)
|
||||
txFee := estimator.fee()
|
||||
|
@ -227,16 +228,20 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
|
|||
// sweep tx has a change output.
|
||||
changeAmt := totalInput - requiredOutput - txFee
|
||||
|
||||
// We'll calculate the dust limit for the given changePkScript since it
|
||||
// is variable.
|
||||
changeLimit := lnwallet.DustLimitForSize(len(changePkScript))
|
||||
|
||||
// The txn will sweep the amount after fees to the pkscript generated
|
||||
// above.
|
||||
if changeAmt >= dustLimit {
|
||||
if changeAmt >= changeLimit {
|
||||
sweepTx.AddTxOut(&wire.TxOut{
|
||||
PkScript: changePkScript,
|
||||
Value: int64(changeAmt),
|
||||
})
|
||||
} else {
|
||||
log.Infof("Change amt %v below dustlimit %v, not adding "+
|
||||
"change output", changeAmt, dustLimit)
|
||||
"change output", changeAmt, changeLimit)
|
||||
}
|
||||
|
||||
// We'll default to using the current block height as locktime, if none
|
||||
|
|
|
@ -165,8 +165,8 @@ type DeliveryAddr struct {
|
|||
// output, as specified by the change address. The sweep transaction will be
|
||||
// crafted with the target fee rate, and will use the utxoSource and
|
||||
// outpointLocker as sources for wallet funds.
|
||||
func CraftSweepAllTx(feeRate chainfee.SatPerKWeight, dustLimit btcutil.Amount,
|
||||
blockHeight uint32, deliveryAddrs []DeliveryAddr, changeAddr btcutil.Address,
|
||||
func CraftSweepAllTx(feeRate chainfee.SatPerKWeight, blockHeight uint32,
|
||||
deliveryAddrs []DeliveryAddr, changeAddr btcutil.Address,
|
||||
coinSelectLocker CoinSelectionLocker, utxoSource UtxoSource,
|
||||
outpointLocker OutpointLocker, feeEstimator chainfee.Estimator,
|
||||
signer input.Signer, minConfs int32) (*WalletSweepPackage, error) {
|
||||
|
@ -302,7 +302,7 @@ func CraftSweepAllTx(feeRate chainfee.SatPerKWeight, dustLimit btcutil.Amount,
|
|||
// respects our fee preference and targets all the UTXOs of the wallet.
|
||||
sweepTx, err := createSweepTx(
|
||||
inputsToSweep, txOuts, changePkScript, blockHeight, feeRate,
|
||||
dustLimit, signer,
|
||||
signer,
|
||||
)
|
||||
if err != nil {
|
||||
unlockOutputs()
|
||||
|
|
|
@ -288,8 +288,8 @@ func TestCraftSweepAllTxCoinSelectFail(t *testing.T) {
|
|||
utxoLocker := newMockOutpointLocker()
|
||||
|
||||
_, err := CraftSweepAllTx(
|
||||
0, 100, 10, nil, nil, coinSelectLocker, utxoSource,
|
||||
utxoLocker, nil, nil, 0,
|
||||
0, 10, nil, nil, coinSelectLocker, utxoSource, utxoLocker, nil,
|
||||
nil, 0,
|
||||
)
|
||||
|
||||
// Since we instructed the coin select locker to fail above, we should
|
||||
|
@ -314,8 +314,8 @@ func TestCraftSweepAllTxUnknownWitnessType(t *testing.T) {
|
|||
utxoLocker := newMockOutpointLocker()
|
||||
|
||||
_, err := CraftSweepAllTx(
|
||||
0, 100, 10, nil, nil, coinSelectLocker, utxoSource,
|
||||
utxoLocker, nil, nil, 0,
|
||||
0, 10, nil, nil, coinSelectLocker, utxoSource, utxoLocker, nil,
|
||||
nil, 0,
|
||||
)
|
||||
|
||||
// Since passed in a p2wsh output, which is unknown, we should fail to
|
||||
|
@ -349,7 +349,7 @@ func TestCraftSweepAllTx(t *testing.T) {
|
|||
utxoLocker := newMockOutpointLocker()
|
||||
|
||||
sweepPkg, err := CraftSweepAllTx(
|
||||
0, 100, 10, nil, deliveryAddr, coinSelectLocker, utxoSource,
|
||||
0, 10, nil, deliveryAddr, coinSelectLocker, utxoSource,
|
||||
utxoLocker, feeEstimator, signer, 0,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -300,7 +300,7 @@ func TestBackupTask(t *testing.T) {
|
|||
expSweepCommitRewardLocal int64 = 197390
|
||||
expSweepCommitRewardRemote int64 = 98437
|
||||
sweepFeeRateNoRewardRemoteDust chainfee.SatPerKWeight = 227500
|
||||
sweepFeeRateRewardRemoteDust chainfee.SatPerKWeight = 175000
|
||||
sweepFeeRateRewardRemoteDust chainfee.SatPerKWeight = 175350
|
||||
)
|
||||
if chanType.HasAnchors() {
|
||||
expSweepCommitNoRewardBoth = 299236
|
||||
|
@ -309,8 +309,8 @@ func TestBackupTask(t *testing.T) {
|
|||
expSweepCommitRewardBoth = 296112
|
||||
expSweepCommitRewardLocal = 197389
|
||||
expSweepCommitRewardRemote = 98433
|
||||
sweepFeeRateNoRewardRemoteDust = 225000
|
||||
sweepFeeRateRewardRemoteDust = 173750
|
||||
sweepFeeRateNoRewardRemoteDust = 225400
|
||||
sweepFeeRateRewardRemoteDust = 174100
|
||||
}
|
||||
|
||||
backupTaskTests = append(backupTaskTests, []backupTaskTest{
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"github.com/btcsuite/btcutil"
|
||||
"github.com/lightningnetwork/lnd/input"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||
"github.com/lightningnetwork/lnd/watchtower/blob"
|
||||
|
@ -165,10 +166,9 @@ func (p *Policy) ComputeAltruistOutput(totalAmt btcutil.Amount,
|
|||
sweepAmt := totalAmt - txFee
|
||||
|
||||
// TODO(conner): replace w/ configurable dust limit
|
||||
dustLimit := lnwallet.DefaultDustLimit()
|
||||
|
||||
// Check that the created outputs won't be dusty.
|
||||
if sweepAmt <= dustLimit {
|
||||
// Check that the created outputs won't be dusty. The sweep pkscript is
|
||||
// currently a p2wpkh, so we'll use that script's dust limit.
|
||||
if sweepAmt < lnwallet.DustLimitForSize(input.P2WPKHSize) {
|
||||
return 0, ErrCreatesDust
|
||||
}
|
||||
|
||||
|
@ -199,10 +199,9 @@ func (p *Policy) ComputeRewardOutputs(totalAmt btcutil.Amount,
|
|||
sweepAmt := totalAmt - rewardAmt - txFee
|
||||
|
||||
// TODO(conner): replace w/ configurable dust limit
|
||||
dustLimit := lnwallet.DefaultDustLimit()
|
||||
|
||||
// Check that the created outputs won't be dusty.
|
||||
if sweepAmt <= dustLimit {
|
||||
// Check that the created outputs won't be dusty. The sweep pkscript is
|
||||
// currently a p2wpkh, so we'll use that script's dust limit.
|
||||
if sweepAmt < lnwallet.DustLimitForSize(input.P2WPKHSize) {
|
||||
return 0, 0, ErrCreatesDust
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue