Merge pull request #3557 from guggero/loop-sweeper-integration
input+sweep: rework witness type into interface to be used externally
This commit is contained in:
12 files changed
+287
-207
No files matched your search
+13
-32
@@ -821,7 +821,7 @@ func (b *breachArbiter) handleBreachHandoff(breachEvent *ContractBreachEvent) {
|
|||||||
type breachedOutput struct {
|
type breachedOutput struct {
|
||||||
amt btcutil.Amount
|
amt btcutil.Amount
|
||||||
outpoint wire.OutPoint
|
outpoint wire.OutPoint
|
||||||
witnessType input.WitnessType
|
witnessType input.StandardWitnessType
|
||||||
signDesc input.SignDescriptor
|
signDesc input.SignDescriptor
|
||||||
confHeight uint32
|
confHeight uint32
|
||||||
|
|
||||||
@@ -833,7 +833,7 @@ type breachedOutput struct {
|
|||||||
// makeBreachedOutput assembles a new breachedOutput that can be used by the
|
// makeBreachedOutput assembles a new breachedOutput that can be used by the
|
||||||
// breach arbiter to construct a justice or sweep transaction.
|
// breach arbiter to construct a justice or sweep transaction.
|
||||||
func makeBreachedOutput(outpoint *wire.OutPoint,
|
func makeBreachedOutput(outpoint *wire.OutPoint,
|
||||||
witnessType input.WitnessType,
|
witnessType input.StandardWitnessType,
|
||||||
secondLevelScript []byte,
|
secondLevelScript []byte,
|
||||||
signDescriptor *input.SignDescriptor,
|
signDescriptor *input.SignDescriptor,
|
||||||
confHeight uint32) breachedOutput {
|
confHeight uint32) breachedOutput {
|
||||||
@@ -883,9 +883,7 @@ func (bo *breachedOutput) CraftInputScript(signer input.Signer, txn *wire.MsgTx,
|
|||||||
|
|
||||||
// First, we ensure that the witness generation function has been
|
// First, we ensure that the witness generation function has been
|
||||||
// initialized for this breached output.
|
// initialized for this breached output.
|
||||||
bo.witnessFunc = bo.witnessType.GenWitnessFunc(
|
bo.witnessFunc = bo.witnessType.WitnessGenerator(signer, bo.SignDesc())
|
||||||
signer, bo.SignDesc(),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Now that we have ensured that the witness generation function has
|
// Now that we have ensured that the witness generation function has
|
||||||
// been initialized, we can proceed to execute it and generate the
|
// been initialized, we can proceed to execute it and generate the
|
||||||
@@ -993,7 +991,7 @@ func newRetributionInfo(chanPoint *wire.OutPoint,
|
|||||||
// Using the breachedHtlc's incoming flag, determine the
|
// Using the breachedHtlc's incoming flag, determine the
|
||||||
// appropriate witness type that needs to be generated in order
|
// appropriate witness type that needs to be generated in order
|
||||||
// to sweep the HTLC output.
|
// to sweep the HTLC output.
|
||||||
var htlcWitnessType input.WitnessType
|
var htlcWitnessType input.StandardWitnessType
|
||||||
if breachedHtlc.IsIncoming {
|
if breachedHtlc.IsIncoming {
|
||||||
htlcWitnessType = input.HtlcAcceptedRevoke
|
htlcWitnessType = input.HtlcAcceptedRevoke
|
||||||
} else {
|
} else {
|
||||||
@@ -1051,32 +1049,15 @@ func (b *breachArbiter) createJusticeTx(
|
|||||||
// Grab locally scoped reference to breached output.
|
// Grab locally scoped reference to breached output.
|
||||||
inp := &r.breachedOutputs[i]
|
inp := &r.breachedOutputs[i]
|
||||||
|
|
||||||
// First, select the appropriate estimated witness weight for
|
// First, determine the appropriate estimated witness weight for
|
||||||
// the give witness type of this breached output. If the witness
|
// the give witness type of this breached output. If the witness
|
||||||
// type is unrecognized, we will omit it from the transaction.
|
// weight cannot be estimated, we will omit it from the
|
||||||
var witnessWeight int
|
// transaction.
|
||||||
switch inp.WitnessType() {
|
witnessWeight, _, err := inp.WitnessType().SizeUpperBound()
|
||||||
case input.CommitSpendNoDelayTweakless:
|
if err != nil {
|
||||||
fallthrough
|
brarLog.Warnf("could not determine witness weight "+
|
||||||
case input.CommitmentNoDelay:
|
"for breached output in retribution info: %v",
|
||||||
witnessWeight = input.P2WKHWitnessSize
|
err)
|
||||||
|
|
||||||
case input.CommitmentRevoke:
|
|
||||||
witnessWeight = input.ToLocalPenaltyWitnessSize
|
|
||||||
|
|
||||||
case input.HtlcOfferedRevoke:
|
|
||||||
witnessWeight = input.OfferedHtlcPenaltyWitnessSize
|
|
||||||
|
|
||||||
case input.HtlcAcceptedRevoke:
|
|
||||||
witnessWeight = input.AcceptedHtlcPenaltyWitnessSize
|
|
||||||
|
|
||||||
case input.HtlcSecondLevelRevoke:
|
|
||||||
witnessWeight = input.ToLocalPenaltyWitnessSize
|
|
||||||
|
|
||||||
default:
|
|
||||||
brarLog.Warnf("breached output in retribution info "+
|
|
||||||
"contains unexpected witness type: %v",
|
|
||||||
inp.WitnessType())
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
weightEstimate.AddWitnessInput(witnessWeight)
|
weightEstimate.AddWitnessInput(witnessWeight)
|
||||||
@@ -1555,7 +1536,7 @@ func (bo *breachedOutput) Decode(r io.Reader) error {
|
|||||||
if _, err := io.ReadFull(r, scratch[:2]); err != nil {
|
if _, err := io.ReadFull(r, scratch[:2]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bo.witnessType = input.WitnessType(
|
bo.witnessType = input.StandardWitnessType(
|
||||||
binary.BigEndian.Uint16(scratch[:2]),
|
binary.BigEndian.Uint16(scratch[:2]),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -114,9 +114,7 @@ func NewBaseInput(outpoint *wire.OutPoint, witnessType WitnessType,
|
|||||||
func (bi *BaseInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
func (bi *BaseInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) (*Script, error) {
|
hashCache *txscript.TxSigHashes, txinIdx int) (*Script, error) {
|
||||||
|
|
||||||
witnessFunc := bi.witnessType.GenWitnessFunc(
|
witnessFunc := bi.witnessType.WitnessGenerator(signer, bi.SignDesc())
|
||||||
signer, bi.SignDesc(),
|
|
||||||
)
|
|
||||||
|
|
||||||
return witnessFunc(txn, hashCache, txinIdx)
|
return witnessFunc(txn, hashCache, txinIdx)
|
||||||
}
|
}
|
||||||
|
|||||||
+173
-30
@@ -7,87 +7,125 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WitnessType determines how an output's witness will be generated. The
|
// WitnessGenerator represents a function that is able to generate the final
|
||||||
// default commitmentTimeLock type will generate a witness that will allow
|
// witness for a particular public key script. Additionally, if required, this
|
||||||
// spending of a time-locked transaction enforced by CheckSequenceVerify.
|
// function will also return the sigScript for spending nested P2SH witness
|
||||||
type WitnessType uint16
|
// outputs. This function acts as an abstraction layer, hiding the details of
|
||||||
|
// the underlying script.
|
||||||
|
type WitnessGenerator func(tx *wire.MsgTx, hc *txscript.TxSigHashes,
|
||||||
|
inputIndex int) (*Script, error)
|
||||||
|
|
||||||
|
// WitnessType determines how an output's witness will be generated. This
|
||||||
|
// interface can be implemented to be used for custom sweep scripts if the
|
||||||
|
// pre-defined StandardWitnessType list doesn't provide a suitable one.
|
||||||
|
type WitnessType interface {
|
||||||
|
// String returns a human readable version of the WitnessType.
|
||||||
|
String() string
|
||||||
|
|
||||||
|
// WitnessGenerator will return a WitnessGenerator function that an
|
||||||
|
// output uses to generate the witness and optionally the sigScript for
|
||||||
|
// a sweep transaction.
|
||||||
|
WitnessGenerator(signer Signer,
|
||||||
|
descriptor *SignDescriptor) WitnessGenerator
|
||||||
|
|
||||||
|
// SizeUpperBound returns the maximum length of the witness of this
|
||||||
|
// WitnessType if it would be included in a tx. It also returns if the
|
||||||
|
// output itself is a nested p2sh output, if so then we need to take
|
||||||
|
// into account the extra sigScript data size.
|
||||||
|
SizeUpperBound() (int, bool, error)
|
||||||
|
|
||||||
|
// AddWeightEstimation adds the estimated size of the witness in bytes
|
||||||
|
// to the given weight estimator and returns the number of
|
||||||
|
// CSVs/CLTVs used by the script.
|
||||||
|
AddWeightEstimation(estimator *TxWeightEstimator) (int, int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StandardWitnessType is a numeric representation of standard pre-defined types
|
||||||
|
// of witness configurations.
|
||||||
|
type StandardWitnessType uint16
|
||||||
|
|
||||||
|
// A compile time check to ensure StandardWitnessType implements the
|
||||||
|
// WitnessType interface.
|
||||||
|
var _ WitnessType = (StandardWitnessType)(0)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// CommitmentTimeLock is a witness that allows us to spend the output of
|
// CommitmentTimeLock is a witness that allows us to spend the output of
|
||||||
// a commitment transaction after a relative lock-time lockout.
|
// a commitment transaction after a relative lock-time lockout.
|
||||||
CommitmentTimeLock WitnessType = 0
|
CommitmentTimeLock StandardWitnessType = 0
|
||||||
|
|
||||||
// CommitmentNoDelay is a witness that allows us to spend a settled
|
// CommitmentNoDelay is a witness that allows us to spend a settled
|
||||||
// no-delay output immediately on a counterparty's commitment
|
// no-delay output immediately on a counterparty's commitment
|
||||||
// transaction.
|
// transaction.
|
||||||
CommitmentNoDelay WitnessType = 1
|
CommitmentNoDelay StandardWitnessType = 1
|
||||||
|
|
||||||
// CommitmentRevoke is a witness that allows us to sweep the settled
|
// CommitmentRevoke is a witness that allows us to sweep the settled
|
||||||
// output of a malicious counterparty's who broadcasts a revoked
|
// output of a malicious counterparty's who broadcasts a revoked
|
||||||
// commitment transaction.
|
// commitment transaction.
|
||||||
CommitmentRevoke WitnessType = 2
|
CommitmentRevoke StandardWitnessType = 2
|
||||||
|
|
||||||
// HtlcOfferedRevoke is a witness that allows us to sweep an HTLC which
|
// HtlcOfferedRevoke is a witness that allows us to sweep an HTLC which
|
||||||
// we offered to the remote party in the case that they broadcast a
|
// we offered to the remote party in the case that they broadcast a
|
||||||
// revoked commitment state.
|
// revoked commitment state.
|
||||||
HtlcOfferedRevoke WitnessType = 3
|
HtlcOfferedRevoke StandardWitnessType = 3
|
||||||
|
|
||||||
// HtlcAcceptedRevoke is a witness that allows us to sweep an HTLC
|
// HtlcAcceptedRevoke is a witness that allows us to sweep an HTLC
|
||||||
// output sent to us in the case that the remote party broadcasts a
|
// output sent to us in the case that the remote party broadcasts a
|
||||||
// revoked commitment state.
|
// revoked commitment state.
|
||||||
HtlcAcceptedRevoke WitnessType = 4
|
HtlcAcceptedRevoke StandardWitnessType = 4
|
||||||
|
|
||||||
// HtlcOfferedTimeoutSecondLevel is a witness that allows us to sweep
|
// HtlcOfferedTimeoutSecondLevel is a witness that allows us to sweep
|
||||||
// an HTLC output that we extended to a party, but was never fulfilled.
|
// an HTLC output that we extended to a party, but was never fulfilled.
|
||||||
// This HTLC output isn't directly on the commitment transaction, but
|
// This HTLC output isn't directly on the commitment transaction, but
|
||||||
// is the result of a confirmed second-level HTLC transaction. As a
|
// is the result of a confirmed second-level HTLC transaction. As a
|
||||||
// result, we can only spend this after a CSV delay.
|
// result, we can only spend this after a CSV delay.
|
||||||
HtlcOfferedTimeoutSecondLevel WitnessType = 5
|
HtlcOfferedTimeoutSecondLevel StandardWitnessType = 5
|
||||||
|
|
||||||
// HtlcAcceptedSuccessSecondLevel is a witness that allows us to sweep
|
// HtlcAcceptedSuccessSecondLevel is a witness that allows us to sweep
|
||||||
// an HTLC output that was offered to us, and for which we have a
|
// an HTLC output that was offered to us, and for which we have a
|
||||||
// payment preimage. This HTLC output isn't directly on our commitment
|
// payment preimage. This HTLC output isn't directly on our commitment
|
||||||
// transaction, but is the result of confirmed second-level HTLC
|
// transaction, but is the result of confirmed second-level HTLC
|
||||||
// transaction. As a result, we can only spend this after a CSV delay.
|
// transaction. As a result, we can only spend this after a CSV delay.
|
||||||
HtlcAcceptedSuccessSecondLevel WitnessType = 6
|
HtlcAcceptedSuccessSecondLevel StandardWitnessType = 6
|
||||||
|
|
||||||
// HtlcOfferedRemoteTimeout is a witness that allows us to sweep an
|
// HtlcOfferedRemoteTimeout is a witness that allows us to sweep an
|
||||||
// HTLC that we offered to the remote party which lies in the
|
// HTLC that we offered to the remote party which lies in the
|
||||||
// commitment transaction of the remote party. We can spend this output
|
// commitment transaction of the remote party. We can spend this output
|
||||||
// after the absolute CLTV timeout of the HTLC as passed.
|
// after the absolute CLTV timeout of the HTLC as passed.
|
||||||
HtlcOfferedRemoteTimeout WitnessType = 7
|
HtlcOfferedRemoteTimeout StandardWitnessType = 7
|
||||||
|
|
||||||
// HtlcAcceptedRemoteSuccess is a witness that allows us to sweep an
|
// HtlcAcceptedRemoteSuccess is a witness that allows us to sweep an
|
||||||
// HTLC that was offered to us by the remote party. We use this witness
|
// HTLC that was offered to us by the remote party. We use this witness
|
||||||
// in the case that the remote party goes to chain, and we know the
|
// in the case that the remote party goes to chain, and we know the
|
||||||
// pre-image to the HTLC. We can sweep this without any additional
|
// pre-image to the HTLC. We can sweep this without any additional
|
||||||
// timeout.
|
// timeout.
|
||||||
HtlcAcceptedRemoteSuccess WitnessType = 8
|
HtlcAcceptedRemoteSuccess StandardWitnessType = 8
|
||||||
|
|
||||||
// HtlcSecondLevelRevoke is a witness that allows us to sweep an HTLC
|
// HtlcSecondLevelRevoke is a witness that allows us to sweep an HTLC
|
||||||
// from the remote party's commitment transaction in the case that the
|
// from the remote party's commitment transaction in the case that the
|
||||||
// broadcast a revoked commitment, but then also immediately attempt to
|
// broadcast a revoked commitment, but then also immediately attempt to
|
||||||
// go to the second level to claim the HTLC.
|
// go to the second level to claim the HTLC.
|
||||||
HtlcSecondLevelRevoke WitnessType = 9
|
HtlcSecondLevelRevoke StandardWitnessType = 9
|
||||||
|
|
||||||
// WitnessKeyHash is a witness type that allows us to spend a regular
|
// WitnessKeyHash is a witness type that allows us to spend a regular
|
||||||
// p2wkh output that's sent to an output which is under complete
|
// p2wkh output that's sent to an output which is under complete
|
||||||
// control of the backing wallet.
|
// control of the backing wallet.
|
||||||
WitnessKeyHash WitnessType = 10
|
WitnessKeyHash StandardWitnessType = 10
|
||||||
|
|
||||||
// NestedWitnessKeyHash is a witness type that allows us to sweep an
|
// NestedWitnessKeyHash is a witness type that allows us to sweep an
|
||||||
// output that sends to a nested P2SH script that pays to a key solely
|
// output that sends to a nested P2SH script that pays to a key solely
|
||||||
// under our control. The witness generated needs to include the
|
// under our control. The witness generated needs to include the
|
||||||
NestedWitnessKeyHash WitnessType = 11
|
NestedWitnessKeyHash StandardWitnessType = 11
|
||||||
|
|
||||||
// CommitSpendNoDelayTweakless is similar to the CommitSpendNoDelay
|
// CommitSpendNoDelayTweakless is similar to the CommitSpendNoDelay
|
||||||
// type, but it omits the tweak that randomizes the key we need to
|
// type, but it omits the tweak that randomizes the key we need to
|
||||||
// spend with a channel peer supplied set of randomness.
|
// spend with a channel peer supplied set of randomness.
|
||||||
CommitSpendNoDelayTweakless = 12
|
CommitSpendNoDelayTweakless StandardWitnessType = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stirng returns a human readable version of the target WitnessType.
|
// String returns a human readable version of the target WitnessType.
|
||||||
func (wt WitnessType) String() string {
|
//
|
||||||
|
// NOTE: This is part of the WitnessType interface.
|
||||||
|
func (wt StandardWitnessType) String() string {
|
||||||
switch wt {
|
switch wt {
|
||||||
case CommitmentTimeLock:
|
case CommitmentTimeLock:
|
||||||
return "CommitmentTimeLock"
|
return "CommitmentTimeLock"
|
||||||
@@ -127,19 +165,13 @@ func (wt WitnessType) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WitnessGenerator represents a function which is able to generate the final
|
// WitnessGenerator will return a WitnessGenerator function that an output uses
|
||||||
// witness for a particular public key script. Additionally, if required, this
|
|
||||||
// function will also return the sigScript for spending nested P2SH witness
|
|
||||||
// outputs. This function acts as an abstraction layer, hiding the details of
|
|
||||||
// the underlying script.
|
|
||||||
type WitnessGenerator func(tx *wire.MsgTx, hc *txscript.TxSigHashes,
|
|
||||||
inputIndex int) (*Script, error)
|
|
||||||
|
|
||||||
// GenWitnessFunc will return a WitnessGenerator function that an output uses
|
|
||||||
// to generate the witness and optionally the sigScript for a sweep
|
// to generate the witness and optionally the sigScript for a sweep
|
||||||
// transaction. The sigScript will be generated if the witness type warrants
|
// transaction. The sigScript will be generated if the witness type warrants
|
||||||
// one for spending, such as the NestedWitnessKeyHash witness type.
|
// one for spending, such as the NestedWitnessKeyHash witness type.
|
||||||
func (wt WitnessType) GenWitnessFunc(signer Signer,
|
//
|
||||||
|
// NOTE: This is part of the WitnessType interface.
|
||||||
|
func (wt StandardWitnessType) WitnessGenerator(signer Signer,
|
||||||
descriptor *SignDescriptor) WitnessGenerator {
|
descriptor *SignDescriptor) WitnessGenerator {
|
||||||
|
|
||||||
return func(tx *wire.MsgTx, hc *txscript.TxSigHashes,
|
return func(tx *wire.MsgTx, hc *txscript.TxSigHashes,
|
||||||
@@ -262,5 +294,116 @@ func (wt WitnessType) GenWitnessFunc(signer Signer,
|
|||||||
return nil, fmt.Errorf("unknown witness type: %v", wt)
|
return nil, fmt.Errorf("unknown witness type: %v", wt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SizeUpperBound returns the maximum length of the witness of this witness
|
||||||
|
// type if it would be included in a tx. We also return if the output itself is
|
||||||
|
// a nested p2sh output, if so then we need to take into account the extra
|
||||||
|
// sigScript data size.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the WitnessType interface.
|
||||||
|
func (wt StandardWitnessType) SizeUpperBound() (int, bool, error) {
|
||||||
|
switch wt {
|
||||||
|
|
||||||
|
// Outputs on a remote commitment transaction that pay directly to us.
|
||||||
|
case CommitSpendNoDelayTweakless:
|
||||||
|
fallthrough
|
||||||
|
case WitnessKeyHash:
|
||||||
|
fallthrough
|
||||||
|
case CommitmentNoDelay:
|
||||||
|
return P2WKHWitnessSize, false, nil
|
||||||
|
|
||||||
|
// Outputs on a past commitment transaction that pay directly
|
||||||
|
// to us.
|
||||||
|
case CommitmentTimeLock:
|
||||||
|
return ToLocalTimeoutWitnessSize, false, nil
|
||||||
|
|
||||||
|
// Outgoing second layer HTLC's that have confirmed within the
|
||||||
|
// chain, and the output they produced is now mature enough to
|
||||||
|
// sweep.
|
||||||
|
case HtlcOfferedTimeoutSecondLevel:
|
||||||
|
return ToLocalTimeoutWitnessSize, false, nil
|
||||||
|
|
||||||
|
// Incoming second layer HTLC's that have confirmed within the
|
||||||
|
// chain, and the output they produced is now mature enough to
|
||||||
|
// sweep.
|
||||||
|
case HtlcAcceptedSuccessSecondLevel:
|
||||||
|
return ToLocalTimeoutWitnessSize, false, nil
|
||||||
|
|
||||||
|
// An HTLC on the commitment transaction of the remote party,
|
||||||
|
// that has had its absolute timelock expire.
|
||||||
|
case HtlcOfferedRemoteTimeout:
|
||||||
|
return AcceptedHtlcTimeoutWitnessSize, false, nil
|
||||||
|
|
||||||
|
// An HTLC on the commitment transaction of the remote party,
|
||||||
|
// that can be swept with the preimage.
|
||||||
|
case HtlcAcceptedRemoteSuccess:
|
||||||
|
return OfferedHtlcSuccessWitnessSize, false, nil
|
||||||
|
|
||||||
|
// A nested P2SH input that has a p2wkh witness script. We'll mark this
|
||||||
|
// as nested P2SH so the caller can estimate the weight properly
|
||||||
|
// including the sigScript.
|
||||||
|
case NestedWitnessKeyHash:
|
||||||
|
return P2WKHWitnessSize, true, nil
|
||||||
|
|
||||||
|
// The revocation output on a revoked commitment transaction.
|
||||||
|
case CommitmentRevoke:
|
||||||
|
return ToLocalPenaltyWitnessSize, false, nil
|
||||||
|
|
||||||
|
// The revocation output on a revoked HTLC that we offered to the remote
|
||||||
|
// party.
|
||||||
|
case HtlcOfferedRevoke:
|
||||||
|
return OfferedHtlcPenaltyWitnessSize, false, nil
|
||||||
|
|
||||||
|
// The revocation output on a revoked HTLC that was sent to us.
|
||||||
|
case HtlcAcceptedRevoke:
|
||||||
|
return AcceptedHtlcPenaltyWitnessSize, false, nil
|
||||||
|
|
||||||
|
// The revocation output of a second level output of an HTLC.
|
||||||
|
case HtlcSecondLevelRevoke:
|
||||||
|
return ToLocalPenaltyWitnessSize, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, false, fmt.Errorf("unexpected witness type: %v", wt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddWeightEstimation adds the estimated size of the witness in bytes to the
|
||||||
|
// given weight estimator and returns the number of CSVs/CLTVs used by the
|
||||||
|
// script.
|
||||||
|
//
|
||||||
|
// NOTE: This is part of the WitnessType interface.
|
||||||
|
func (wt StandardWitnessType) AddWeightEstimation(
|
||||||
|
estimator *TxWeightEstimator) (int, int, error) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
csvCount = 0
|
||||||
|
cltvCount = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// For fee estimation purposes, we'll now attempt to obtain an
|
||||||
|
// upper bound on the weight this input will add when fully
|
||||||
|
// populated.
|
||||||
|
size, isNestedP2SH, err := wt.SizeUpperBound()
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a nested P2SH input, then we'll need to factor in
|
||||||
|
// the additional data push within the sigScript.
|
||||||
|
if isNestedP2SH {
|
||||||
|
estimator.AddNestedP2WSHInput(size)
|
||||||
|
} else {
|
||||||
|
estimator.AddWitnessInput(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch wt {
|
||||||
|
case CommitmentTimeLock,
|
||||||
|
HtlcOfferedTimeoutSecondLevel,
|
||||||
|
HtlcAcceptedSuccessSecondLevel:
|
||||||
|
csvCount++
|
||||||
|
case HtlcOfferedRemoteTimeout:
|
||||||
|
cltvCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
return csvCount, cltvCount, nil
|
||||||
}
|
}
|
||||||
@@ -764,7 +764,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
|
|||||||
sweep.DefaultBatchWindowDuration)
|
sweep.DefaultBatchWindowDuration)
|
||||||
|
|
||||||
sweeperStore, err := sweep.NewSweeperStore(
|
sweeperStore, err := sweep.NewSweeperStore(
|
||||||
chanDB, activeNetParams.GenesisHash,
|
chanDB.DB, activeNetParams.GenesisHash,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
srvrLog.Errorf("unable to create sweeper store: %v", err)
|
srvrLog.Errorf("unable to create sweeper store: %v", err)
|
||||||
@@ -780,7 +780,6 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
|
|||||||
return time.NewTimer(sweep.DefaultBatchWindowDuration).C
|
return time.NewTimer(sweep.DefaultBatchWindowDuration).C
|
||||||
},
|
},
|
||||||
Notifier: cc.chainNotifier,
|
Notifier: cc.chainNotifier,
|
||||||
ChainIO: cc.chainIO,
|
|
||||||
Store: sweeperStore,
|
Store: sweeperStore,
|
||||||
MaxInputsPerTx: sweep.DefaultMaxInputsPerTx,
|
MaxInputsPerTx: sweep.DefaultMaxInputsPerTx,
|
||||||
MaxSweepAttempts: sweep.DefaultMaxSweepAttempts,
|
MaxSweepAttempts: sweep.DefaultMaxSweepAttempts,
|
||||||
|
|||||||
+2
-3
@@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/coreos/bbolt"
|
"github.com/coreos/bbolt"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -57,11 +56,11 @@ type SweeperStore interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type sweeperStore struct {
|
type sweeperStore struct {
|
||||||
db *channeldb.DB
|
db *bbolt.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSweeperStore returns a new store instance.
|
// NewSweeperStore returns a new store instance.
|
||||||
func NewSweeperStore(db *channeldb.DB, chainHash *chainhash.Hash) (
|
func NewSweeperStore(db *bbolt.DB, chainHash *chainhash.Hash) (
|
||||||
SweeperStore, error) {
|
SweeperStore, error) {
|
||||||
|
|
||||||
err := db.Update(func(tx *bbolt.Tx) error {
|
err := db.Update(func(tx *bbolt.Tx) error {
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ func TestStore(t *testing.T) {
|
|||||||
|
|
||||||
testStore(t, func() (SweeperStore, error) {
|
testStore(t, func() (SweeperStore, error) {
|
||||||
var chain chainhash.Hash
|
var chain chainhash.Hash
|
||||||
return NewSweeperStore(cdb, &chain)
|
return NewSweeperStore(cdb.DB, &chain)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("mock", func(t *testing.T) {
|
t.Run("mock", func(t *testing.T) {
|
||||||
|
|||||||
+19
-25
@@ -212,9 +212,6 @@ type UtxoSweeperConfig struct {
|
|||||||
// certain on-chain events.
|
// certain on-chain events.
|
||||||
Notifier chainntnfs.ChainNotifier
|
Notifier chainntnfs.ChainNotifier
|
||||||
|
|
||||||
// ChainIO is used to determine the current block height.
|
|
||||||
ChainIO lnwallet.BlockChainIO
|
|
||||||
|
|
||||||
// Store stores the published sweeper txes.
|
// Store stores the published sweeper txes.
|
||||||
Store SweeperStore
|
Store SweeperStore
|
||||||
|
|
||||||
@@ -323,20 +320,10 @@ func (s *UtxoSweeper) Start() error {
|
|||||||
// not change from here on.
|
// not change from here on.
|
||||||
s.relayFeeRate = s.cfg.FeeEstimator.RelayFeePerKW()
|
s.relayFeeRate = s.cfg.FeeEstimator.RelayFeePerKW()
|
||||||
|
|
||||||
// Register for block epochs to retry sweeping every block.
|
// We need to register for block epochs and retry sweeping every block.
|
||||||
bestHash, bestHeight, err := s.cfg.ChainIO.GetBestBlock()
|
// We should get a notification with the current best block immediately
|
||||||
if err != nil {
|
// if we don't provide any epoch. We'll wait for that in the collector.
|
||||||
return fmt.Errorf("get best block: %v", err)
|
blockEpochs, err := s.cfg.Notifier.RegisterBlockEpochNtfn(nil)
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("Best height: %v", bestHeight)
|
|
||||||
|
|
||||||
blockEpochs, err := s.cfg.Notifier.RegisterBlockEpochNtfn(
|
|
||||||
&chainntnfs.BlockEpoch{
|
|
||||||
Height: bestHeight,
|
|
||||||
Hash: bestHash,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("register block epoch ntfn: %v", err)
|
return fmt.Errorf("register block epoch ntfn: %v", err)
|
||||||
}
|
}
|
||||||
@@ -347,10 +334,7 @@ func (s *UtxoSweeper) Start() error {
|
|||||||
defer blockEpochs.Cancel()
|
defer blockEpochs.Cancel()
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
|
|
||||||
err := s.collector(blockEpochs.Epochs, bestHeight)
|
s.collector(blockEpochs.Epochs)
|
||||||
if err != nil {
|
|
||||||
log.Errorf("sweeper stopped: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -445,8 +429,18 @@ func (s *UtxoSweeper) feeRateForPreference(
|
|||||||
|
|
||||||
// collector is the sweeper main loop. It processes new inputs, spend
|
// collector is the sweeper main loop. It processes new inputs, spend
|
||||||
// notifications and counts down to publication of the sweep tx.
|
// notifications and counts down to publication of the sweep tx.
|
||||||
func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch,
|
func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch) {
|
||||||
bestHeight int32) error {
|
// We registered for the block epochs with a nil request. The notifier
|
||||||
|
// should send us the current best block immediately. So we need to wait
|
||||||
|
// for it here because we need to know the current best height.
|
||||||
|
var bestHeight int32
|
||||||
|
select {
|
||||||
|
case bestBlock := <-blockEpochs:
|
||||||
|
bestHeight = bestBlock.Height
|
||||||
|
|
||||||
|
case <-s.quit:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -622,7 +616,7 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch,
|
|||||||
// sweep.
|
// sweep.
|
||||||
case epoch, ok := <-blockEpochs:
|
case epoch, ok := <-blockEpochs:
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bestHeight = epoch.Height
|
bestHeight = epoch.Height
|
||||||
@@ -635,7 +629,7 @@ func (s *UtxoSweeper) collector(blockEpochs <-chan *chainntnfs.BlockEpoch,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case <-s.quit:
|
case <-s.quit:
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,9 +130,8 @@ func createSweeperTestContext(t *testing.T) *sweeperTestContext {
|
|||||||
ctx.timeoutChan <- c
|
ctx.timeoutChan <- c
|
||||||
return c
|
return c
|
||||||
},
|
},
|
||||||
Store: store,
|
Store: store,
|
||||||
Signer: &mockSigner{},
|
Signer: &mockSigner{},
|
||||||
ChainIO: &mockChainIO{},
|
|
||||||
GenSweepScript: func() ([]byte, error) {
|
GenSweepScript: func() ([]byte, error) {
|
||||||
script := []byte{outputScriptCount}
|
script := []byte{outputScriptCount}
|
||||||
outputScriptCount++
|
outputScriptCount++
|
||||||
|
|||||||
+17
-29
@@ -10,12 +10,12 @@ import (
|
|||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/chainntnfs"
|
"github.com/lightningnetwork/lnd/chainntnfs"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultTestTimeout = 5 * time.Second
|
defaultTestTimeout = 5 * time.Second
|
||||||
mockChainIOHeight = int32(100)
|
mockChainHash, _ = chainhash.NewHashFromStr("00aabbccddeeff")
|
||||||
|
mockChainHeight = int32(100)
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockSigner struct {
|
type mockSigner struct {
|
||||||
@@ -155,12 +155,22 @@ func (m *MockNotifier) RegisterBlockEpochNtfn(
|
|||||||
log.Tracef("Mock block ntfn registered")
|
log.Tracef("Mock block ntfn registered")
|
||||||
|
|
||||||
m.mutex.Lock()
|
m.mutex.Lock()
|
||||||
epochChan := make(chan *chainntnfs.BlockEpoch, 0)
|
epochChan := make(chan *chainntnfs.BlockEpoch, 1)
|
||||||
bestHeight := int32(0)
|
|
||||||
if bestBlock != nil {
|
// The real notifier returns a notification with the current block hash
|
||||||
bestHeight = bestBlock.Height
|
// and height immediately if no best block hash or height is specified
|
||||||
|
// in the request. We want to emulate this behaviour as well for the
|
||||||
|
// mock.
|
||||||
|
switch {
|
||||||
|
case bestBlock == nil:
|
||||||
|
epochChan <- &chainntnfs.BlockEpoch{
|
||||||
|
Hash: mockChainHash,
|
||||||
|
Height: mockChainHeight,
|
||||||
|
}
|
||||||
|
m.epochChan[epochChan] = mockChainHeight
|
||||||
|
default:
|
||||||
|
m.epochChan[epochChan] = bestBlock.Height
|
||||||
}
|
}
|
||||||
m.epochChan[epochChan] = bestHeight
|
|
||||||
m.mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
|
|
||||||
return &chainntnfs.BlockEpochEvent{
|
return &chainntnfs.BlockEpochEvent{
|
||||||
@@ -235,25 +245,3 @@ func (m *MockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
|
|||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockChainIO struct{}
|
|
||||||
|
|
||||||
var _ lnwallet.BlockChainIO = (*mockChainIO)(nil)
|
|
||||||
|
|
||||||
func (m *mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) {
|
|
||||||
return nil, mockChainIOHeight, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockChainIO) GetUtxo(op *wire.OutPoint, pkScript []byte,
|
|
||||||
heightHint uint32, _ <-chan struct{}) (*wire.TxOut, error) {
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockChainIO) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockChainIO) GetBlock(blockHash *chainhash.Hash) (*wire.MsgBlock, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
+6
-76
@@ -56,7 +56,7 @@ func generateInputPartitionings(sweepableInputs []input.Input,
|
|||||||
// on the signature length, which is not known yet at this point.
|
// on the signature length, which is not known yet at this point.
|
||||||
yields := make(map[wire.OutPoint]int64)
|
yields := make(map[wire.OutPoint]int64)
|
||||||
for _, input := range sweepableInputs {
|
for _, input := range sweepableInputs {
|
||||||
size, _, err := getInputWitnessSizeUpperBound(input)
|
size, _, err := input.WitnessType().SizeUpperBound()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"failed adding input weight: %v", err)
|
"failed adding input weight: %v", err)
|
||||||
@@ -126,7 +126,7 @@ func getPositiveYieldInputs(sweepableInputs []input.Input, maxInputs int,
|
|||||||
for idx, input := range sweepableInputs {
|
for idx, input := range sweepableInputs {
|
||||||
// Can ignore error, because it has already been checked when
|
// Can ignore error, because it has already been checked when
|
||||||
// calculating the yields.
|
// calculating the yields.
|
||||||
size, isNestedP2SH, _ := getInputWitnessSizeUpperBound(input)
|
size, isNestedP2SH, _ := input.WitnessType().SizeUpperBound()
|
||||||
|
|
||||||
// Keep a running weight estimate of the input set.
|
// Keep a running weight estimate of the input set.
|
||||||
if isNestedP2SH {
|
if isNestedP2SH {
|
||||||
@@ -251,59 +251,6 @@ func createSweepTx(inputs []input.Input, outputPkScript []byte,
|
|||||||
return sweepTx, nil
|
return sweepTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInputWitnessSizeUpperBound returns the maximum length of the witness for
|
|
||||||
// the given input if it would be included in a tx. We also return if the
|
|
||||||
// output itself is a nested p2sh output, if so then we need to take into
|
|
||||||
// account the extra sigScript data size.
|
|
||||||
func getInputWitnessSizeUpperBound(inp input.Input) (int, bool, error) {
|
|
||||||
switch inp.WitnessType() {
|
|
||||||
|
|
||||||
// Outputs on a remote commitment transaction that pay directly to us.
|
|
||||||
case input.CommitSpendNoDelayTweakless:
|
|
||||||
fallthrough
|
|
||||||
case input.WitnessKeyHash:
|
|
||||||
fallthrough
|
|
||||||
case input.CommitmentNoDelay:
|
|
||||||
return input.P2WKHWitnessSize, false, nil
|
|
||||||
|
|
||||||
// Outputs on a past commitment transaction that pay directly
|
|
||||||
// to us.
|
|
||||||
case input.CommitmentTimeLock:
|
|
||||||
return input.ToLocalTimeoutWitnessSize, false, nil
|
|
||||||
|
|
||||||
// Outgoing second layer HTLC's that have confirmed within the
|
|
||||||
// chain, and the output they produced is now mature enough to
|
|
||||||
// sweep.
|
|
||||||
case input.HtlcOfferedTimeoutSecondLevel:
|
|
||||||
return input.ToLocalTimeoutWitnessSize, false, nil
|
|
||||||
|
|
||||||
// Incoming second layer HTLC's that have confirmed within the
|
|
||||||
// chain, and the output they produced is now mature enough to
|
|
||||||
// sweep.
|
|
||||||
case input.HtlcAcceptedSuccessSecondLevel:
|
|
||||||
return input.ToLocalTimeoutWitnessSize, false, nil
|
|
||||||
|
|
||||||
// An HTLC on the commitment transaction of the remote party,
|
|
||||||
// that has had its absolute timelock expire.
|
|
||||||
case input.HtlcOfferedRemoteTimeout:
|
|
||||||
return input.AcceptedHtlcTimeoutWitnessSize, false, nil
|
|
||||||
|
|
||||||
// An HTLC on the commitment transaction of the remote party,
|
|
||||||
// that can be swept with the preimage.
|
|
||||||
case input.HtlcAcceptedRemoteSuccess:
|
|
||||||
return input.OfferedHtlcSuccessWitnessSize, false, nil
|
|
||||||
|
|
||||||
// A nested P2SH input that has a p2wkh witness script. We'll mark this
|
|
||||||
// as nested P2SH so the caller can estimate the weight properly
|
|
||||||
// including the sigScript.
|
|
||||||
case input.NestedWitnessKeyHash:
|
|
||||||
return input.P2WKHWitnessSize, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, false, fmt.Errorf("unexpected witness type: %v",
|
|
||||||
inp.WitnessType())
|
|
||||||
}
|
|
||||||
|
|
||||||
// getWeightEstimate returns a weight estimate for the given inputs.
|
// getWeightEstimate returns a weight estimate for the given inputs.
|
||||||
// Additionally, it returns counts for the number of csv and cltv inputs.
|
// Additionally, it returns counts for the number of csv and cltv inputs.
|
||||||
func getWeightEstimate(inputs []input.Input) ([]input.Input, int64, int, int) {
|
func getWeightEstimate(inputs []input.Input) ([]input.Input, int64, int, int) {
|
||||||
@@ -328,10 +275,8 @@ func getWeightEstimate(inputs []input.Input) ([]input.Input, int64, int, int) {
|
|||||||
for i := range inputs {
|
for i := range inputs {
|
||||||
inp := inputs[i]
|
inp := inputs[i]
|
||||||
|
|
||||||
// For fee estimation purposes, we'll now attempt to obtain an
|
wt := inp.WitnessType()
|
||||||
// upper bound on the weight this input will add when fully
|
inpCsv, inpCltv, err := wt.AddWeightEstimation(&weightEstimate)
|
||||||
// populated.
|
|
||||||
size, isNestedP2SH, err := getInputWitnessSizeUpperBound(inp)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn(err)
|
log.Warn(err)
|
||||||
|
|
||||||
@@ -339,23 +284,8 @@ func getWeightEstimate(inputs []input.Input) ([]input.Input, int64, int, int) {
|
|||||||
// given.
|
// given.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
csvCount += inpCsv
|
||||||
// If this is a nested P2SH input, then we'll need to factor in
|
cltvCount += inpCltv
|
||||||
// the additional data push within the sigScript.
|
|
||||||
if isNestedP2SH {
|
|
||||||
weightEstimate.AddNestedP2WSHInput(size)
|
|
||||||
} else {
|
|
||||||
weightEstimate.AddWitnessInput(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch inp.WitnessType() {
|
|
||||||
case input.CommitmentTimeLock,
|
|
||||||
input.HtlcOfferedTimeoutSecondLevel,
|
|
||||||
input.HtlcAcceptedSuccessSecondLevel:
|
|
||||||
csvCount++
|
|
||||||
case input.HtlcOfferedRemoteTimeout:
|
|
||||||
cltvCount++
|
|
||||||
}
|
|
||||||
sweepInputs = append(sweepInputs, inp)
|
sweepInputs = append(sweepInputs, inp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package sweep
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/lightningnetwork/lnd/input"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
witnessTypes = []input.WitnessType{
|
||||||
|
input.CommitmentTimeLock,
|
||||||
|
input.HtlcAcceptedSuccessSecondLevel,
|
||||||
|
input.HtlcOfferedRemoteTimeout,
|
||||||
|
input.WitnessKeyHash,
|
||||||
|
}
|
||||||
|
expectedWeight = int64(1459)
|
||||||
|
expectedCsv = 2
|
||||||
|
expectedCltv = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestWeightEstimate tests that the estimated weight and number of CSVs/CLTVs
|
||||||
|
// used is correct for a transaction that uses inputs with the witness types
|
||||||
|
// defined in witnessTypes.
|
||||||
|
func TestWeightEstimate(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var inputs []input.Input
|
||||||
|
for _, witnessType := range witnessTypes {
|
||||||
|
inputs = append(inputs, input.NewBaseInput(
|
||||||
|
&wire.OutPoint{}, witnessType,
|
||||||
|
&input.SignDescriptor{}, 0,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
_, weight, csv, cltv := getWeightEstimate(inputs)
|
||||||
|
if weight != expectedWeight {
|
||||||
|
t.Fatalf("unexpected weight. expected %d but got %d.",
|
||||||
|
expectedWeight, weight)
|
||||||
|
}
|
||||||
|
if csv != expectedCsv {
|
||||||
|
t.Fatalf("unexpected csv count. expected %d but got %d.",
|
||||||
|
expectedCsv, csv)
|
||||||
|
}
|
||||||
|
if cltv != expectedCltv {
|
||||||
|
t.Fatalf("unexpected cltv count. expected %d but got %d.",
|
||||||
|
expectedCltv, cltv)
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -1344,7 +1344,7 @@ type kidOutput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeKidOutput(outpoint, originChanPoint *wire.OutPoint,
|
func makeKidOutput(outpoint, originChanPoint *wire.OutPoint,
|
||||||
blocksToMaturity uint32, witnessType input.WitnessType,
|
blocksToMaturity uint32, witnessType input.StandardWitnessType,
|
||||||
signDescriptor *input.SignDescriptor,
|
signDescriptor *input.SignDescriptor,
|
||||||
absoluteMaturity uint32) kidOutput {
|
absoluteMaturity uint32) kidOutput {
|
||||||
|
|
||||||
@@ -1423,7 +1423,7 @@ func (k *kidOutput) Encode(w io.Writer) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
byteOrder.PutUint16(scratch[:2], uint16(k.WitnessType()))
|
byteOrder.PutUint16(scratch[:2], uint16(k.witnessType))
|
||||||
if _, err := w.Write(scratch[:2]); err != nil {
|
if _, err := w.Write(scratch[:2]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1473,7 +1473,7 @@ func (k *kidOutput) Decode(r io.Reader) error {
|
|||||||
if _, err := r.Read(scratch[:2]); err != nil {
|
if _, err := r.Read(scratch[:2]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
k.witnessType = input.WitnessType(byteOrder.Uint16(scratch[:2]))
|
k.witnessType = input.StandardWitnessType(byteOrder.Uint16(scratch[:2]))
|
||||||
|
|
||||||
return input.ReadSignDescriptor(r, &k.signDesc)
|
return input.ReadSignDescriptor(r, &k.signDesc)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user