mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-02-20 13:34:32 +01:00
lnwallet: support transactions and scripts for new commitment type
This commit modifies the channel state machine to be able to derive the proper commitment and second-level HTLC output scripts required by the new script-enforced leased channel commitment type.
This commit is contained in:
parent
01e9bb2bff
commit
8cfb53f64a
11 changed files with 230 additions and 98 deletions
|
@ -2299,6 +2299,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
|
|||
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
|
||||
channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
|
||||
bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
|
||||
false, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
|
|
@ -362,8 +362,13 @@ func (c *chainWatcher) handleUnknownLocalState(
|
|||
|
||||
// With the keys derived, we'll construct the remote script that'll be
|
||||
// present if they have a non-dust balance on the commitment.
|
||||
var leaseExpiry uint32
|
||||
if c.cfg.chanState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = c.cfg.chanState.ThawHeight
|
||||
}
|
||||
remoteScript, _, err := lnwallet.CommitScriptToRemote(
|
||||
c.cfg.chanState.ChanType, commitKeyRing.ToRemoteKey,
|
||||
c.cfg.chanState.ChanType, c.cfg.chanState.IsInitiator,
|
||||
commitKeyRing.ToRemoteKey, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -373,8 +378,9 @@ func (c *chainWatcher) handleUnknownLocalState(
|
|||
// the remote party allowing them to claim this output before the CSV
|
||||
// delay if we breach.
|
||||
localScript, err := lnwallet.CommitScriptToSelf(
|
||||
c.cfg.chanState.ChanType, c.cfg.chanState.IsInitiator,
|
||||
commitKeyRing.ToLocalKey, commitKeyRing.RevocationKey,
|
||||
uint32(c.cfg.chanState.LocalChanCfg.CsvDelay),
|
||||
uint32(c.cfg.chanState.LocalChanCfg.CsvDelay), leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -134,6 +134,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
|
|||
channelCapacity := aliceAmount + bobAmount
|
||||
csvTimeoutAlice := uint32(5)
|
||||
csvTimeoutBob := uint32(4)
|
||||
isAliceInitiator := true
|
||||
|
||||
aliceConstraints := &channeldb.ChannelConstraints{
|
||||
DustLimit: btcutil.Amount(200),
|
||||
|
@ -230,6 +231,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
|
|||
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
|
||||
aliceAmount, bobAmount, &aliceCfg, &bobCfg, aliceCommitPoint,
|
||||
bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
|
||||
isAliceInitiator, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
@ -298,7 +300,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
|
|||
IdentityPub: aliceKeyPub,
|
||||
FundingOutpoint: *prevOut,
|
||||
ChanType: channeldb.SingleFunderTweaklessBit,
|
||||
IsInitiator: true,
|
||||
IsInitiator: isAliceInitiator,
|
||||
Capacity: channelCapacity,
|
||||
RemoteCurrentRevocation: bobCommitPoint,
|
||||
RevocationProducer: alicePreimageProducer,
|
||||
|
@ -317,7 +319,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
|
|||
IdentityPub: bobKeyPub,
|
||||
FundingOutpoint: *prevOut,
|
||||
ChanType: channeldb.SingleFunderTweaklessBit,
|
||||
IsInitiator: false,
|
||||
IsInitiator: !isAliceInitiator,
|
||||
Capacity: channelCapacity,
|
||||
RemoteCurrentRevocation: aliceCommitPoint,
|
||||
RevocationProducer: bobPreimageProducer,
|
||||
|
|
|
@ -861,8 +861,8 @@ func TestWitnessSizes(t *testing.T) {
|
|||
func genTimeoutTx(chanType channeldb.ChannelType) (*wire.MsgTx, error) {
|
||||
// Create the unsigned timeout tx.
|
||||
timeoutTx, err := lnwallet.CreateHtlcTimeoutTx(
|
||||
chanType, testOutPoint, testAmt, testCLTVExpiry,
|
||||
testCSVDelay, testPubkey, testPubkey,
|
||||
chanType, false, testOutPoint, testAmt, testCLTVExpiry,
|
||||
testCSVDelay, 0, testPubkey, testPubkey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -903,7 +903,7 @@ func genTimeoutTx(chanType channeldb.ChannelType) (*wire.MsgTx, error) {
|
|||
func genSuccessTx(chanType channeldb.ChannelType) (*wire.MsgTx, error) {
|
||||
// Create the unisgned success tx.
|
||||
successTx, err := lnwallet.CreateHtlcSuccessTx(
|
||||
chanType, testOutPoint, testAmt, testCSVDelay,
|
||||
chanType, false, testOutPoint, testAmt, testCSVDelay, 0,
|
||||
testPubkey, testPubkey,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -2315,8 +2315,14 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||
// number so we can have the proper witness script to sign and include
|
||||
// within the final witness.
|
||||
theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
|
||||
isRemoteInitiator := !chanState.IsInitiator
|
||||
var leaseExpiry uint32
|
||||
if chanState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = chanState.ThawHeight
|
||||
}
|
||||
theirScript, err := CommitScriptToSelf(
|
||||
keyRing.ToLocalKey, keyRing.RevocationKey, theirDelay,
|
||||
chanState.ChanType, isRemoteInitiator, keyRing.ToLocalKey,
|
||||
keyRing.RevocationKey, theirDelay, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -2325,7 +2331,8 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||
// Since it is the remote breach we are reconstructing, the output going
|
||||
// to us will be a to-remote script with our local params.
|
||||
ourScript, ourDelay, err := CommitScriptToRemote(
|
||||
chanState.ChanType, keyRing.ToRemoteKey,
|
||||
chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey,
|
||||
leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -2410,7 +2417,9 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
|
|||
// remote commitment transaction, and *they* go to the second
|
||||
// level.
|
||||
secondLevelScript, err := SecondLevelHtlcScript(
|
||||
chanState.ChanType, isRemoteInitiator,
|
||||
keyRing.RevocationKey, keyRing.ToLocalKey, theirDelay,
|
||||
leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -2968,8 +2977,8 @@ func processFeeUpdate(feeUpdate *PaymentDescriptor, nextHeight uint64,
|
|||
// signature can be submitted to the sigPool to generate all the signatures
|
||||
// asynchronously and in parallel.
|
||||
func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
|
||||
chanType channeldb.ChannelType,
|
||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||
chanType channeldb.ChannelType, isRemoteInitiator bool,
|
||||
leaseExpiry uint32, localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||
remoteCommitView *commitment) ([]SignJob, chan struct{}, error) {
|
||||
|
||||
txHash := remoteCommitView.txn.TxHash()
|
||||
|
@ -3019,9 +3028,9 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
|
|||
Index: uint32(htlc.remoteOutputIndex),
|
||||
}
|
||||
sigJob.Tx, err = CreateHtlcTimeoutTx(
|
||||
chanType, op, outputAmt, htlc.Timeout,
|
||||
uint32(remoteChanCfg.CsvDelay),
|
||||
keyRing.RevocationKey, keyRing.ToLocalKey,
|
||||
chanType, isRemoteInitiator, op, outputAmt,
|
||||
htlc.Timeout, uint32(remoteChanCfg.CsvDelay),
|
||||
leaseExpiry, keyRing.RevocationKey, keyRing.ToLocalKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -3072,7 +3081,8 @@ func genRemoteHtlcSigJobs(keyRing *CommitmentKeyRing,
|
|||
Index: uint32(htlc.remoteOutputIndex),
|
||||
}
|
||||
sigJob.Tx, err = CreateHtlcSuccessTx(
|
||||
chanType, op, outputAmt, uint32(remoteChanCfg.CsvDelay),
|
||||
chanType, isRemoteInitiator, op, outputAmt,
|
||||
uint32(remoteChanCfg.CsvDelay), leaseExpiry,
|
||||
keyRing.RevocationKey, keyRing.ToLocalKey,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -3581,10 +3591,14 @@ func (lc *LightningChannel) SignNextCommitment() (lnwire.Sig, []lnwire.Sig, []ch
|
|||
// need to generate signatures of each of them for the remote party's
|
||||
// commitment state. We do so in two phases: first we generate and
|
||||
// submit the set of signature jobs to the worker pool.
|
||||
var leaseExpiry uint32
|
||||
if lc.channelState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = lc.channelState.ThawHeight
|
||||
}
|
||||
sigBatch, cancelChan, err := genRemoteHtlcSigJobs(
|
||||
keyRing, lc.channelState.ChanType,
|
||||
&lc.channelState.LocalChanCfg, &lc.channelState.RemoteChanCfg,
|
||||
newCommitView,
|
||||
keyRing, lc.channelState.ChanType, !lc.channelState.IsInitiator,
|
||||
leaseExpiry, &lc.channelState.LocalChanCfg,
|
||||
&lc.channelState.RemoteChanCfg, newCommitView,
|
||||
)
|
||||
if err != nil {
|
||||
return sig, htlcSigs, nil, err
|
||||
|
@ -4067,7 +4081,7 @@ func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
|
|||
// directly into the pool of workers.
|
||||
func genHtlcSigValidationJobs(localCommitmentView *commitment,
|
||||
keyRing *CommitmentKeyRing, htlcSigs []lnwire.Sig,
|
||||
chanType channeldb.ChannelType,
|
||||
chanType channeldb.ChannelType, isLocalInitiator bool, leaseExpiry uint32,
|
||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig) ([]VerifyJob, error) {
|
||||
|
||||
txHash := localCommitmentView.txn.TxHash()
|
||||
|
@ -4116,9 +4130,10 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
|
|||
outputAmt := htlc.Amount.ToSatoshis() - htlcFee
|
||||
|
||||
successTx, err := CreateHtlcSuccessTx(
|
||||
chanType, op, outputAmt,
|
||||
uint32(localChanCfg.CsvDelay),
|
||||
keyRing.RevocationKey, keyRing.ToLocalKey,
|
||||
chanType, isLocalInitiator, op,
|
||||
outputAmt, uint32(localChanCfg.CsvDelay),
|
||||
leaseExpiry, keyRing.RevocationKey,
|
||||
keyRing.ToLocalKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -4170,8 +4185,9 @@ func genHtlcSigValidationJobs(localCommitmentView *commitment,
|
|||
outputAmt := htlc.Amount.ToSatoshis() - htlcFee
|
||||
|
||||
timeoutTx, err := CreateHtlcTimeoutTx(
|
||||
chanType, op, outputAmt, htlc.Timeout,
|
||||
uint32(localChanCfg.CsvDelay),
|
||||
chanType, isLocalInitiator, op,
|
||||
outputAmt, htlc.Timeout,
|
||||
uint32(localChanCfg.CsvDelay), leaseExpiry,
|
||||
keyRing.RevocationKey, keyRing.ToLocalKey,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -4385,9 +4401,14 @@ func (lc *LightningChannel) ReceiveNewCommitment(commitSig lnwire.Sig,
|
|||
// As an optimization, we'll generate a series of jobs for the worker
|
||||
// pool to verify each of the HTLc signatures presented. Once
|
||||
// generated, we'll submit these jobs to the worker pool.
|
||||
var leaseExpiry uint32
|
||||
if lc.channelState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = lc.channelState.ThawHeight
|
||||
}
|
||||
verifyJobs, err := genHtlcSigValidationJobs(
|
||||
localCommitmentView, keyRing, htlcSigs,
|
||||
lc.channelState.ChanType, &lc.channelState.LocalChanCfg,
|
||||
lc.channelState.ChanType, lc.channelState.IsInitiator,
|
||||
leaseExpiry, &lc.channelState.LocalChanCfg,
|
||||
&lc.channelState.RemoteChanCfg,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -5570,18 +5591,24 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||
|
||||
// First, we'll generate the commitment point and the revocation point
|
||||
// so we can re-construct the HTLC state and also our payment key.
|
||||
isOurCommit := false
|
||||
keyRing := DeriveCommitmentKeys(
|
||||
commitPoint, false, chanState.ChanType,
|
||||
commitPoint, isOurCommit, chanState.ChanType,
|
||||
&chanState.LocalChanCfg, &chanState.RemoteChanCfg,
|
||||
)
|
||||
|
||||
// Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we
|
||||
// had on their commitment transaction.
|
||||
var leaseExpiry uint32
|
||||
if chanState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = chanState.ThawHeight
|
||||
}
|
||||
isRemoteInitiator := !chanState.IsInitiator
|
||||
htlcResolutions, err := extractHtlcResolutions(
|
||||
chainfee.SatPerKWeight(remoteCommit.FeePerKw), false, signer,
|
||||
remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
||||
chainfee.SatPerKWeight(remoteCommit.FeePerKw), isOurCommit,
|
||||
signer, remoteCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
||||
&chanState.RemoteChanCfg, commitSpend.SpendingTx,
|
||||
chanState.ChanType,
|
||||
chanState.ChanType, isRemoteInitiator, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create htlc "+
|
||||
|
@ -5594,7 +5621,8 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
|
|||
// locate the output index of our non-delayed output on the commitment
|
||||
// transaction.
|
||||
selfScript, maturityDelay, err := CommitScriptToRemote(
|
||||
chanState.ChanType, keyRing.ToRemoteKey,
|
||||
chanState.ChanType, isRemoteInitiator, keyRing.ToRemoteKey,
|
||||
leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create self commit "+
|
||||
|
@ -5801,8 +5829,9 @@ type HtlcResolutions struct {
|
|||
func newOutgoingHtlcResolution(signer input.Signer,
|
||||
localChanCfg *channeldb.ChannelConfig, commitTx *wire.MsgTx,
|
||||
htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
|
||||
feePerKw chainfee.SatPerKWeight, csvDelay uint32,
|
||||
localCommit bool, chanType channeldb.ChannelType) (*OutgoingHtlcResolution, error) {
|
||||
feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32,
|
||||
localCommit, isCommitFromInitiator bool,
|
||||
chanType channeldb.ChannelType) (*OutgoingHtlcResolution, error) {
|
||||
|
||||
op := wire.OutPoint{
|
||||
Hash: commitTx.TxHash(),
|
||||
|
@ -5854,8 +5883,9 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
// With the fee calculated, re-construct the second level timeout
|
||||
// transaction.
|
||||
timeoutTx, err := CreateHtlcTimeoutTx(
|
||||
chanType, op, secondLevelOutputAmt, htlc.RefundTimeout,
|
||||
csvDelay, keyRing.RevocationKey, keyRing.ToLocalKey,
|
||||
chanType, isCommitFromInitiator, op, secondLevelOutputAmt,
|
||||
htlc.RefundTimeout, csvDelay, leaseExpiry, keyRing.RevocationKey,
|
||||
keyRing.ToLocalKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -5901,7 +5931,8 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
// transaction creates so we can generate the signDesc required to
|
||||
// complete the claim process after a delay period.
|
||||
htlcSweepScript, err := SecondLevelHtlcScript(
|
||||
keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay,
|
||||
chanType, isCommitFromInitiator, keyRing.RevocationKey,
|
||||
keyRing.ToLocalKey, csvDelay, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -5942,8 +5973,9 @@ func newOutgoingHtlcResolution(signer input.Signer,
|
|||
func newIncomingHtlcResolution(signer input.Signer,
|
||||
localChanCfg *channeldb.ChannelConfig, commitTx *wire.MsgTx,
|
||||
htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
|
||||
feePerKw chainfee.SatPerKWeight, csvDelay uint32, localCommit bool,
|
||||
chanType channeldb.ChannelType) (*IncomingHtlcResolution, error) {
|
||||
feePerKw chainfee.SatPerKWeight, csvDelay, leaseExpiry uint32,
|
||||
localCommit, isCommitFromInitiator bool, chanType channeldb.ChannelType) (
|
||||
*IncomingHtlcResolution, error) {
|
||||
|
||||
op := wire.OutPoint{
|
||||
Hash: commitTx.TxHash(),
|
||||
|
@ -5988,8 +6020,8 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||
htlcFee := HtlcSuccessFee(chanType, feePerKw)
|
||||
secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee
|
||||
successTx, err := CreateHtlcSuccessTx(
|
||||
chanType, op, secondLevelOutputAmt, csvDelay,
|
||||
keyRing.RevocationKey, keyRing.ToLocalKey,
|
||||
chanType, isCommitFromInitiator, op, secondLevelOutputAmt,
|
||||
csvDelay, leaseExpiry, keyRing.RevocationKey, keyRing.ToLocalKey,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6036,7 +6068,8 @@ func newIncomingHtlcResolution(signer input.Signer,
|
|||
// creates so we can generate the proper signDesc to sweep it after the
|
||||
// CSV delay has passed.
|
||||
htlcSweepScript, err := SecondLevelHtlcScript(
|
||||
keyRing.RevocationKey, keyRing.ToLocalKey, csvDelay,
|
||||
chanType, isCommitFromInitiator, keyRing.RevocationKey,
|
||||
keyRing.ToLocalKey, csvDelay, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6096,8 +6129,8 @@ func (r *OutgoingHtlcResolution) HtlcPoint() wire.OutPoint {
|
|||
func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool,
|
||||
signer input.Signer, htlcs []channeldb.HTLC, keyRing *CommitmentKeyRing,
|
||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||
commitTx *wire.MsgTx, chanType channeldb.ChannelType) (
|
||||
*HtlcResolutions, error) {
|
||||
commitTx *wire.MsgTx, chanType channeldb.ChannelType,
|
||||
isCommitFromInitiator bool, leaseExpiry uint32) (*HtlcResolutions, error) {
|
||||
|
||||
// TODO(roasbeef): don't need to swap csv delay?
|
||||
dustLimit := remoteChanCfg.DustLimit
|
||||
|
@ -6129,8 +6162,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool,
|
|||
// as we can satisfy the contract.
|
||||
ihr, err := newIncomingHtlcResolution(
|
||||
signer, localChanCfg, commitTx, &htlc,
|
||||
keyRing, feePerKw, uint32(csvDelay), ourCommit,
|
||||
chanType,
|
||||
keyRing, feePerKw, uint32(csvDelay), leaseExpiry,
|
||||
ourCommit, isCommitFromInitiator, chanType,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6142,7 +6175,8 @@ func extractHtlcResolutions(feePerKw chainfee.SatPerKWeight, ourCommit bool,
|
|||
|
||||
ohr, err := newOutgoingHtlcResolution(
|
||||
signer, localChanCfg, commitTx, &htlc, keyRing,
|
||||
feePerKw, uint32(csvDelay), ourCommit, chanType,
|
||||
feePerKw, uint32(csvDelay), leaseExpiry, ourCommit,
|
||||
isCommitFromInitiator, chanType,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6281,8 +6315,13 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||
&chanState.LocalChanCfg, &chanState.RemoteChanCfg,
|
||||
)
|
||||
|
||||
var leaseExpiry uint32
|
||||
if chanState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = chanState.ThawHeight
|
||||
}
|
||||
toLocalScript, err := CommitScriptToSelf(
|
||||
keyRing.ToLocalKey, keyRing.RevocationKey, csvTimeout,
|
||||
chanState.ChanType, chanState.IsInitiator, keyRing.ToLocalKey,
|
||||
keyRing.RevocationKey, csvTimeout, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -6343,6 +6382,7 @@ func NewLocalForceCloseSummary(chanState *channeldb.OpenChannel,
|
|||
chainfee.SatPerKWeight(localCommit.FeePerKw), true, signer,
|
||||
localCommit.Htlcs, keyRing, &chanState.LocalChanCfg,
|
||||
&chanState.RemoteChanCfg, commitTx, chanState.ChanType,
|
||||
chanState.IsInitiator, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -192,14 +192,31 @@ type ScriptInfo struct {
|
|||
|
||||
// CommitScriptToSelf constructs the public key script for the output on the
|
||||
// commitment transaction paying to the "owner" of said commitment transaction.
|
||||
// If the other party learns of the preimage to the revocation hash, then they
|
||||
// can claim all the settled funds in the channel, plus the unsettled funds.
|
||||
func CommitScriptToSelf(selfKey, revokeKey *btcec.PublicKey, csvDelay uint32) (
|
||||
// The `initiator` argument should correspond to the owner of the commitment
|
||||
// tranasction which we are generating the to_local script for. If the other
|
||||
// party learns of the preimage to the revocation hash, then they can claim all
|
||||
// the settled funds in the channel, plus the unsettled funds.
|
||||
func CommitScriptToSelf(chanType channeldb.ChannelType, initiator bool,
|
||||
selfKey, revokeKey *btcec.PublicKey, csvDelay, leaseExpiry uint32) (
|
||||
*ScriptInfo, error) {
|
||||
|
||||
toLocalRedeemScript, err := input.CommitScriptToSelf(
|
||||
csvDelay, selfKey, revokeKey,
|
||||
var (
|
||||
toLocalRedeemScript []byte
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
// If we are the initiator of a leased channel, then we have an
|
||||
// additional CLTV requirement in addition to the usual CSV requirement.
|
||||
case initiator && chanType.HasLeaseExpiration():
|
||||
toLocalRedeemScript, err = input.LeaseCommitScriptToSelf(
|
||||
selfKey, revokeKey, csvDelay, leaseExpiry,
|
||||
)
|
||||
|
||||
default:
|
||||
toLocalRedeemScript, err = input.CommitScriptToSelf(
|
||||
csvDelay, selfKey, revokeKey,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -216,14 +233,38 @@ func CommitScriptToSelf(selfKey, revokeKey *btcec.PublicKey, csvDelay uint32) (
|
|||
}
|
||||
|
||||
// CommitScriptToRemote derives the appropriate to_remote script based on the
|
||||
// channel's commitment type. The second return value is the CSV delay of the
|
||||
// output script, what must be satisfied in order to spend the output.
|
||||
func CommitScriptToRemote(chanType channeldb.ChannelType,
|
||||
key *btcec.PublicKey) (*ScriptInfo, uint32, error) {
|
||||
// channel's commitment type. The `initiator` argument should correspond to the
|
||||
// owner of the commitment tranasction which we are generating the to_remote
|
||||
// script for. The second return value is the CSV delay of the output script,
|
||||
// what must be satisfied in order to spend the output.
|
||||
func CommitScriptToRemote(chanType channeldb.ChannelType, initiator bool,
|
||||
key *btcec.PublicKey, leaseExpiry uint32) (*ScriptInfo, uint32, error) {
|
||||
|
||||
switch {
|
||||
// If we are not the initiator of a leased channel, then the remote
|
||||
// party has an additional CLTV requirement in addition to the 1 block
|
||||
// CSV requirement.
|
||||
case chanType.HasLeaseExpiration() && !initiator:
|
||||
script, err := input.LeaseCommitScriptToRemoteConfirmed(
|
||||
key, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
p2wsh, err := input.WitnessScriptHash(script)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &ScriptInfo{
|
||||
PkScript: p2wsh,
|
||||
WitnessScript: script,
|
||||
}, 1, nil
|
||||
|
||||
// If this channel type has anchors, we derive the delayed to_remote
|
||||
// script.
|
||||
if chanType.HasAnchors() {
|
||||
case chanType.HasAnchors():
|
||||
script, err := input.CommitScriptToRemoteConfirmed(key)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
|
@ -238,20 +279,22 @@ func CommitScriptToRemote(chanType channeldb.ChannelType,
|
|||
PkScript: p2wsh,
|
||||
WitnessScript: script,
|
||||
}, 1, nil
|
||||
}
|
||||
|
||||
// Otherwise the to_remote will be a simple p2wkh.
|
||||
p2wkh, err := input.CommitScriptUnencumbered(key)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
default:
|
||||
// Otherwise the to_remote will be a simple p2wkh.
|
||||
p2wkh, err := input.CommitScriptUnencumbered(key)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// Since this is a regular P2WKH, the WitnessScipt and PkScript should
|
||||
// both be set to the script hash.
|
||||
return &ScriptInfo{
|
||||
WitnessScript: p2wkh,
|
||||
PkScript: p2wkh,
|
||||
}, 0, nil
|
||||
// Since this is a regular P2WKH, the WitnessScipt and PkScript
|
||||
// should both be set to the script hash.
|
||||
return &ScriptInfo{
|
||||
WitnessScript: p2wkh,
|
||||
PkScript: p2wkh,
|
||||
}, 0, nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// HtlcSigHashType returns the sighash type to use for HTLC success and timeout
|
||||
|
@ -296,13 +339,30 @@ func HtlcSecondLevelInputSequence(chanType channeldb.ChannelType) uint32 {
|
|||
// on the channel's commitment type. It is the uniform script that's used as the
|
||||
// output for the second-level HTLC transactions. The second level transaction
|
||||
// act as a sort of covenant, ensuring that a 2-of-2 multi-sig output can only
|
||||
// be spent in a particular way, and to a particular output.
|
||||
func SecondLevelHtlcScript(revocationKey, delayKey *btcec.PublicKey,
|
||||
csvDelay uint32) (*ScriptInfo, error) {
|
||||
// be spent in a particular way, and to a particular output. The `initiator`
|
||||
// argument should correspond to the owner of the commitment tranasction which
|
||||
// we are generating the to_local script for.
|
||||
func SecondLevelHtlcScript(chanType channeldb.ChannelType, initiator bool,
|
||||
revocationKey, delayKey *btcec.PublicKey,
|
||||
csvDelay, leaseExpiry uint32) (*ScriptInfo, error) {
|
||||
|
||||
witnessScript, err := input.SecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay,
|
||||
var (
|
||||
witnessScript []byte
|
||||
err error
|
||||
)
|
||||
switch {
|
||||
// If we are the initiator of a leased channel, then we have an
|
||||
// additional CLTV requirement in addition to the usual CSV requirement.
|
||||
case initiator && chanType.HasLeaseExpiration():
|
||||
witnessScript, err = input.LeaseSecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay, leaseExpiry,
|
||||
)
|
||||
|
||||
default:
|
||||
witnessScript, err = input.SecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -549,19 +609,23 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
|
|||
// CreateCommitTx with parameters matching the perspective, to generate
|
||||
// a new commitment transaction with all the latest unsettled/un-timed
|
||||
// out HTLCs.
|
||||
var leaseExpiry uint32
|
||||
if cb.chanState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = cb.chanState.ThawHeight
|
||||
}
|
||||
if isOurs {
|
||||
commitTx, err = CreateCommitTx(
|
||||
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
|
||||
&cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg,
|
||||
ourBalance.ToSatoshis(), theirBalance.ToSatoshis(),
|
||||
numHTLCs,
|
||||
numHTLCs, cb.chanState.IsInitiator, leaseExpiry,
|
||||
)
|
||||
} else {
|
||||
commitTx, err = CreateCommitTx(
|
||||
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
|
||||
&cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg,
|
||||
theirBalance.ToSatoshis(), ourBalance.ToSatoshis(),
|
||||
numHTLCs,
|
||||
numHTLCs, !cb.chanState.IsInitiator, leaseExpiry,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -660,12 +724,13 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
|
|||
// output paying to the "owner" of the commitment transaction which can be
|
||||
// spent after a relative block delay or revocation event, and a remote output
|
||||
// paying the counterparty within the channel, which can be spent immediately
|
||||
// or after a delay depending on the commitment type..
|
||||
// or after a delay depending on the commitment type. The `initiator` argument
|
||||
// should correspond to the owner of the commitment tranasction we are creating.
|
||||
func CreateCommitTx(chanType channeldb.ChannelType,
|
||||
fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
|
||||
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
|
||||
amountToLocal, amountToRemote btcutil.Amount,
|
||||
numHTLCs int64) (*wire.MsgTx, error) {
|
||||
numHTLCs int64, initiator bool, leaseExpiry uint32) (*wire.MsgTx, error) {
|
||||
|
||||
// First, we create the script for the delayed "pay-to-self" output.
|
||||
// This output has 2 main redemption clauses: either we can redeem the
|
||||
|
@ -673,8 +738,8 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
|||
// the funds with the revocation key if we broadcast a revoked
|
||||
// commitment transaction.
|
||||
toLocalScript, err := CommitScriptToSelf(
|
||||
keyRing.ToLocalKey, keyRing.RevocationKey,
|
||||
uint32(localChanCfg.CsvDelay),
|
||||
chanType, initiator, keyRing.ToLocalKey, keyRing.RevocationKey,
|
||||
uint32(localChanCfg.CsvDelay), leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -682,7 +747,7 @@ func CreateCommitTx(chanType channeldb.ChannelType,
|
|||
|
||||
// Next, we create the script paying to the remote.
|
||||
toRemoteScript, _, err := CommitScriptToRemote(
|
||||
chanType, keyRing.ToRemoteKey,
|
||||
chanType, initiator, keyRing.ToRemoteKey, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -119,6 +119,7 @@ func CreateTestChannels(chanType channeldb.ChannelType) (
|
|||
channelBal := channelCapacity / 2
|
||||
csvTimeoutAlice := uint32(5)
|
||||
csvTimeoutBob := uint32(4)
|
||||
isAliceInitiator := true
|
||||
|
||||
prevOut := &wire.OutPoint{
|
||||
Hash: chainhash.Hash(testHdSeed),
|
||||
|
@ -223,7 +224,7 @@ func CreateTestChannels(chanType channeldb.ChannelType) (
|
|||
|
||||
aliceCommitTx, bobCommitTx, err := CreateCommitmentTxns(
|
||||
channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
|
||||
bobCommitPoint, *fundingTxIn, chanType,
|
||||
bobCommitPoint, *fundingTxIn, chanType, isAliceInitiator, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
@ -318,7 +319,7 @@ func CreateTestChannels(chanType channeldb.ChannelType) (
|
|||
FundingOutpoint: *prevOut,
|
||||
ShortChannelID: shortChanID,
|
||||
ChanType: chanType,
|
||||
IsInitiator: true,
|
||||
IsInitiator: isAliceInitiator,
|
||||
Capacity: channelCapacity,
|
||||
RemoteCurrentRevocation: bobCommitPoint,
|
||||
RevocationProducer: alicePreimageProducer,
|
||||
|
@ -336,7 +337,7 @@ func CreateTestChannels(chanType channeldb.ChannelType) (
|
|||
FundingOutpoint: *prevOut,
|
||||
ShortChannelID: shortChanID,
|
||||
ChanType: chanType,
|
||||
IsInitiator: false,
|
||||
IsInitiator: !isAliceInitiator,
|
||||
Capacity: channelCapacity,
|
||||
RemoteCurrentRevocation: aliceCommitPoint,
|
||||
RevocationProducer: bobPreimageProducer,
|
||||
|
|
|
@ -44,9 +44,10 @@ var (
|
|||
// In order to spend the HTLC output, the witness for the passed transaction
|
||||
// should be:
|
||||
// * <0> <sender sig> <recvr sig> <preimage>
|
||||
func CreateHtlcSuccessTx(chanType channeldb.ChannelType,
|
||||
htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, csvDelay uint32,
|
||||
revocationKey, delayKey *btcec.PublicKey) (*wire.MsgTx, error) {
|
||||
func CreateHtlcSuccessTx(chanType channeldb.ChannelType, initiator bool,
|
||||
htlcOutput wire.OutPoint, htlcAmt btcutil.Amount, csvDelay,
|
||||
leaseExpiry uint32, revocationKey, delayKey *btcec.PublicKey) (
|
||||
*wire.MsgTx, error) {
|
||||
|
||||
// Create a version two transaction (as the success version of this
|
||||
// spends an output with a CSV timeout).
|
||||
|
@ -65,7 +66,8 @@ func CreateHtlcSuccessTx(chanType channeldb.ChannelType,
|
|||
// level HTLC which forces a covenant w.r.t what can be done with all
|
||||
// HTLC outputs.
|
||||
script, err := SecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay,
|
||||
chanType, initiator, revocationKey, delayKey, csvDelay,
|
||||
leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -97,9 +99,9 @@ func CreateHtlcSuccessTx(chanType channeldb.ChannelType,
|
|||
// NOTE: The passed amount for the HTLC should take into account the required
|
||||
// fee rate at the time the HTLC was created. The fee should be able to
|
||||
// entirely pay for this (tiny: 1-in 1-out) transaction.
|
||||
func CreateHtlcTimeoutTx(chanType channeldb.ChannelType,
|
||||
func CreateHtlcTimeoutTx(chanType channeldb.ChannelType, initiator bool,
|
||||
htlcOutput wire.OutPoint, htlcAmt btcutil.Amount,
|
||||
cltvExpiry, csvDelay uint32,
|
||||
cltvExpiry, csvDelay, leaseExpiry uint32,
|
||||
revocationKey, delayKey *btcec.PublicKey) (*wire.MsgTx, error) {
|
||||
|
||||
// Create a version two transaction (as the success version of this
|
||||
|
@ -123,7 +125,8 @@ func CreateHtlcTimeoutTx(chanType channeldb.ChannelType,
|
|||
// level HTLC which forces a covenant w.r.t what can be done with all
|
||||
// HTLC outputs.
|
||||
script, err := SecondLevelHtlcScript(
|
||||
revocationKey, delayKey, csvDelay,
|
||||
chanType, initiator, revocationKey, delayKey, csvDelay,
|
||||
leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -573,7 +573,7 @@ func testSpendValidation(t *testing.T, tweakless bool) {
|
|||
}
|
||||
commitmentTx, err := CreateCommitTx(
|
||||
channelType, *fakeFundingTxIn, keyRing, aliceChanCfg,
|
||||
bobChanCfg, channelBalance, channelBalance, 0,
|
||||
bobChanCfg, channelBalance, channelBalance, 0, true, 0,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create commitment transaction: %v", nil)
|
||||
|
@ -889,7 +889,7 @@ func createTestChannelsForVectors(tc *testContext, chanType channeldb.ChannelTyp
|
|||
remoteCommitTx, localCommitTx, err := CreateCommitmentTxns(
|
||||
remoteBalance, localBalance-commitFee,
|
||||
&remoteCfg, &localCfg, remoteCommitPoint,
|
||||
localCommitPoint, *fundingTxIn, chanType,
|
||||
localCommitPoint, *fundingTxIn, chanType, true, 0,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
|
|
@ -1246,8 +1246,8 @@ func (l *LightningWallet) handleFundingCancelRequest(req *fundingReserveCancelMs
|
|||
func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
|
||||
ourChanCfg, theirChanCfg *channeldb.ChannelConfig,
|
||||
localCommitPoint, remoteCommitPoint *btcec.PublicKey,
|
||||
fundingTxIn wire.TxIn, chanType channeldb.ChannelType) (
|
||||
*wire.MsgTx, *wire.MsgTx, error) {
|
||||
fundingTxIn wire.TxIn, chanType channeldb.ChannelType, initiator bool,
|
||||
leaseExpiry uint32) (*wire.MsgTx, *wire.MsgTx, error) {
|
||||
|
||||
localCommitmentKeys := DeriveCommitmentKeys(
|
||||
localCommitPoint, true, chanType, ourChanCfg, theirChanCfg,
|
||||
|
@ -1258,7 +1258,8 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
|
|||
|
||||
ourCommitTx, err := CreateCommitTx(
|
||||
chanType, fundingTxIn, localCommitmentKeys, ourChanCfg,
|
||||
theirChanCfg, localBalance, remoteBalance, 0,
|
||||
theirChanCfg, localBalance, remoteBalance, 0, initiator,
|
||||
leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -1271,7 +1272,8 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
|
|||
|
||||
theirCommitTx, err := CreateCommitTx(
|
||||
chanType, fundingTxIn, remoteCommitmentKeys, theirChanCfg,
|
||||
ourChanCfg, remoteBalance, localBalance, 0,
|
||||
ourChanCfg, remoteBalance, localBalance, 0, !initiator,
|
||||
leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -1522,12 +1524,17 @@ func (l *LightningWallet) handleChanPointReady(req *continueContributionMsg) {
|
|||
// With the funding tx complete, create both commitment transactions.
|
||||
localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis()
|
||||
remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis()
|
||||
var leaseExpiry uint32
|
||||
if pendingReservation.partialState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = pendingReservation.partialState.ThawHeight
|
||||
}
|
||||
ourCommitTx, theirCommitTx, err := CreateCommitmentTxns(
|
||||
localBalance, remoteBalance, ourContribution.ChannelConfig,
|
||||
theirContribution.ChannelConfig,
|
||||
ourContribution.FirstCommitmentPoint,
|
||||
theirContribution.FirstCommitmentPoint, fundingTxIn,
|
||||
pendingReservation.partialState.ChanType,
|
||||
pendingReservation.partialState.IsInitiator, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
|
@ -1894,6 +1901,10 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
|||
// remote node's commitment transactions.
|
||||
localBalance := pendingReservation.partialState.LocalCommitment.LocalBalance.ToSatoshis()
|
||||
remoteBalance := pendingReservation.partialState.LocalCommitment.RemoteBalance.ToSatoshis()
|
||||
var leaseExpiry uint32
|
||||
if pendingReservation.partialState.ChanType.HasLeaseExpiration() {
|
||||
leaseExpiry = pendingReservation.partialState.ThawHeight
|
||||
}
|
||||
ourCommitTx, theirCommitTx, err := CreateCommitmentTxns(
|
||||
localBalance, remoteBalance,
|
||||
pendingReservation.ourContribution.ChannelConfig,
|
||||
|
@ -1901,6 +1912,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
|
|||
pendingReservation.ourContribution.FirstCommitmentPoint,
|
||||
pendingReservation.theirContribution.FirstCommitmentPoint,
|
||||
*fundingTxIn, pendingReservation.partialState.ChanType,
|
||||
pendingReservation.partialState.IsInitiator, leaseExpiry,
|
||||
)
|
||||
if err != nil {
|
||||
req.err <- err
|
||||
|
|
|
@ -79,6 +79,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
|
|||
bobDustLimit := btcutil.Amount(1300)
|
||||
csvTimeoutAlice := uint32(5)
|
||||
csvTimeoutBob := uint32(4)
|
||||
isAliceInitiator := true
|
||||
|
||||
prevOut := &wire.OutPoint{
|
||||
Hash: channels.TestHdSeed,
|
||||
|
@ -162,6 +163,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
|
|||
aliceCommitTx, bobCommitTx, err := lnwallet.CreateCommitmentTxns(
|
||||
channelBal, channelBal, &aliceCfg, &bobCfg, aliceCommitPoint,
|
||||
bobCommitPoint, *fundingTxIn, channeldb.SingleFunderTweaklessBit,
|
||||
isAliceInitiator, 0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
|
@ -229,7 +231,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
|
|||
FundingOutpoint: *prevOut,
|
||||
ShortChannelID: shortChanID,
|
||||
ChanType: channeldb.SingleFunderTweaklessBit,
|
||||
IsInitiator: true,
|
||||
IsInitiator: isAliceInitiator,
|
||||
Capacity: channelCapacity,
|
||||
RemoteCurrentRevocation: bobCommitPoint,
|
||||
RevocationProducer: alicePreimageProducer,
|
||||
|
@ -246,7 +248,7 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
|
|||
IdentityPub: bobKeyPub,
|
||||
FundingOutpoint: *prevOut,
|
||||
ChanType: channeldb.SingleFunderTweaklessBit,
|
||||
IsInitiator: false,
|
||||
IsInitiator: !isAliceInitiator,
|
||||
Capacity: channelCapacity,
|
||||
RemoteCurrentRevocation: aliceCommitPoint,
|
||||
RevocationProducer: bobPreimageProducer,
|
||||
|
|
Loading…
Add table
Reference in a new issue