lnwallet: use channel type to derive remote script

Based on the current channel type, we derive the script used for the
to_remote output. Currently only the unencumbered p2wkh type is used,
but that will change with upcoming channel types.
This commit is contained in:
Johan T. Halseth 2020-01-06 11:42:04 +01:00
parent 9b5809a884
commit a56ed72bd7
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26
5 changed files with 105 additions and 62 deletions

@ -351,8 +351,9 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
// With the keys derived, we'll construct the remote script that'll be
// present if they have a non-dust balance on the commitment.
remotePkScript, err := input.CommitScriptUnencumbered(
commitKeyRing.ToRemoteKey,
remoteDelay := uint32(remoteChanCfg.CsvDelay)
remoteScript, err := lnwallet.CommitScriptToRemote(
chanType, remoteDelay, commitKeyRing.ToRemoteKey,
)
if err != nil {
return false, err
@ -383,7 +384,7 @@ func isOurCommitment(localChanCfg, remoteChanCfg channeldb.ChannelConfig,
case bytes.Equal(localPkScript, pkScript):
return true, nil
case bytes.Equal(remotePkScript, pkScript):
case bytes.Equal(remoteScript.PkScript, pkScript):
return true, nil
}
}

@ -1871,36 +1871,42 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// Next, reconstruct the scripts as they were present at this state
// number so we can have the proper witness script to sign and include
// within the final witness.
remoteDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
remotePkScript, err := input.CommitScriptToSelf(
remoteDelay, keyRing.ToLocalKey, keyRing.RevocationKey,
theirDelay := uint32(chanState.RemoteChanCfg.CsvDelay)
theirPkScript, err := input.CommitScriptToSelf(
theirDelay, keyRing.ToLocalKey, keyRing.RevocationKey,
)
if err != nil {
return nil, err
}
remoteWitnessHash, err := input.WitnessScriptHash(remotePkScript)
theirWitnessHash, err := input.WitnessScriptHash(theirPkScript)
if err != nil {
return nil, err
}
localPkScript, err := input.CommitScriptUnencumbered(keyRing.ToRemoteKey)
// Since it is the remote breach we are reconstructing, the output going
// to us will be a to-remote script with our local params.
ourDelay := uint32(chanState.LocalChanCfg.CsvDelay)
ourScript, err := CommitScriptToRemote(
chanState.ChanType, ourDelay, keyRing.ToRemoteKey,
)
if err != nil {
return nil, err
}
// In order to fully populate the breach retribution struct, we'll need
// to find the exact index of the local+remote commitment outputs.
localOutpoint := wire.OutPoint{
// to find the exact index of the commitment outputs.
ourOutpoint := wire.OutPoint{
Hash: commitHash,
}
remoteOutpoint := wire.OutPoint{
theirOutpoint := wire.OutPoint{
Hash: commitHash,
}
for i, txOut := range revokedSnapshot.CommitTx.TxOut {
switch {
case bytes.Equal(txOut.PkScript, localPkScript):
localOutpoint.Index = uint32(i)
case bytes.Equal(txOut.PkScript, remoteWitnessHash):
remoteOutpoint.Index = uint32(i)
case bytes.Equal(txOut.PkScript, ourScript.PkScript):
ourOutpoint.Index = uint32(i)
case bytes.Equal(txOut.PkScript, theirWitnessHash):
theirOutpoint.Index = uint32(i)
}
}
@ -1908,39 +1914,39 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// commitment outputs. If either is considered dust using the remote
// party's dust limit, the respective sign descriptor will be nil.
var (
localSignDesc *input.SignDescriptor
remoteSignDesc *input.SignDescriptor
ourSignDesc *input.SignDescriptor
theirSignDesc *input.SignDescriptor
)
// Compute the local and remote balances in satoshis.
localAmt := revokedSnapshot.LocalBalance.ToSatoshis()
remoteAmt := revokedSnapshot.RemoteBalance.ToSatoshis()
// Compute the balances in satoshis.
ourAmt := revokedSnapshot.LocalBalance.ToSatoshis()
theirAmt := revokedSnapshot.RemoteBalance.ToSatoshis()
// If the local balance exceeds the remote party's dust limit,
// instantiate the local sign descriptor.
if localAmt >= chanState.RemoteChanCfg.DustLimit {
localSignDesc = &input.SignDescriptor{
// If our balance exceeds the remote party's dust limit, instantiate
// the sign descriptor for our output.
if ourAmt >= chanState.RemoteChanCfg.DustLimit {
ourSignDesc = &input.SignDescriptor{
SingleTweak: keyRing.LocalCommitKeyTweak,
KeyDesc: chanState.LocalChanCfg.PaymentBasePoint,
WitnessScript: localPkScript,
WitnessScript: ourScript.WitnessScript,
Output: &wire.TxOut{
PkScript: localPkScript,
Value: int64(localAmt),
PkScript: ourScript.PkScript,
Value: int64(ourAmt),
},
HashType: txscript.SigHashAll,
}
}
// Similarly, if the remote balance exceeds the remote party's dust
// limit, assemble the remote sign descriptor.
if remoteAmt >= chanState.RemoteChanCfg.DustLimit {
remoteSignDesc = &input.SignDescriptor{
// Similarly, if their balance exceeds the remote party's dust limit,
// assemble the sign descriptor for their output, which we can sweep.
if theirAmt >= chanState.RemoteChanCfg.DustLimit {
theirSignDesc = &input.SignDescriptor{
KeyDesc: chanState.LocalChanCfg.RevocationBasePoint,
DoubleTweak: commitmentSecret,
WitnessScript: remotePkScript,
WitnessScript: theirPkScript,
Output: &wire.TxOut{
PkScript: remoteWitnessHash,
Value: int64(remoteAmt),
PkScript: theirWitnessHash,
Value: int64(theirAmt),
},
HashType: txscript.SigHashAll,
}
@ -1971,7 +1977,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// remote commitment transaction, and *they* go to the second
// level.
secondLevelWitnessScript, err := input.SecondLevelHtlcScript(
keyRing.RevocationKey, keyRing.ToLocalKey, remoteDelay,
keyRing.RevocationKey, keyRing.ToLocalKey, theirDelay,
)
if err != nil {
return nil, err
@ -2037,13 +2043,13 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
BreachHeight: breachHeight,
RevokedStateNum: stateNum,
PendingHTLCs: revokedSnapshot.Htlcs,
LocalOutpoint: localOutpoint,
LocalOutputSignDesc: localSignDesc,
RemoteOutpoint: remoteOutpoint,
RemoteOutputSignDesc: remoteSignDesc,
LocalOutpoint: ourOutpoint,
LocalOutputSignDesc: ourSignDesc,
RemoteOutpoint: theirOutpoint,
RemoteOutputSignDesc: theirSignDesc,
HtlcRetributions: htlcRetributions,
KeyRing: keyRing,
RemoteDelay: remoteDelay,
RemoteDelay: theirDelay,
}, nil
}
@ -4758,7 +4764,10 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
// Before we can generate the proper sign descriptor, we'll need to
// locate the output index of our non-delayed output on the commitment
// transaction.
selfP2WKH, err := input.CommitScriptUnencumbered(keyRing.ToRemoteKey)
localDelay := uint32(chanState.LocalChanCfg.CsvDelay)
selfScript, err := CommitScriptToRemote(
chanState.ChanType, localDelay, keyRing.ToRemoteKey,
)
if err != nil {
return nil, fmt.Errorf("unable to create self commit "+
"script: %v", err)
@ -4770,7 +4779,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
)
for outputIndex, txOut := range commitTxBroadcast.TxOut {
if bytes.Equal(txOut.PkScript, selfP2WKH) {
if bytes.Equal(txOut.PkScript, selfScript.PkScript) {
selfPoint = &wire.OutPoint{
Hash: *commitSpend.SpenderTxHash,
Index: uint32(outputIndex),
@ -4791,10 +4800,10 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer input.Si
SelfOutputSignDesc: input.SignDescriptor{
KeyDesc: localPayBase,
SingleTweak: keyRing.LocalCommitKeyTweak,
WitnessScript: selfP2WKH,
WitnessScript: selfScript.WitnessScript,
Output: &wire.TxOut{
Value: localBalance,
PkScript: selfP2WKH,
PkScript: selfScript.PkScript,
},
HashType: txscript.SigHashAll,
},

@ -162,6 +162,37 @@ func DeriveCommitmentKeys(commitPoint *btcec.PublicKey,
return keyRing
}
// ScriptInfo holds a redeem script and hash.
type ScriptInfo struct {
// PkScript is the output's PkScript.
PkScript []byte
// WitnessScript is the full script required to properly redeem the
// output. This field should be set to the full script if a p2wsh
// output is being signed. For p2wkh it should be set equal to the
// PkScript.
WitnessScript []byte
}
// CommitScriptToRemote creates the script that will pay to the non-owner of
// the commitment transaction, adding a delay to the script based on the
// channel type.
func CommitScriptToRemote(_ channeldb.ChannelType, csvTimeout uint32,
key *btcec.PublicKey) (*ScriptInfo, error) {
p2wkh, err := input.CommitScriptUnencumbered(key)
if err != nil {
return nil, 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,
}, nil
}
// CommitmentBuilder is a type that wraps the type of channel we are dealing
// with, and abstracts the various ways of constructing commitment
// transactions.
@ -292,15 +323,15 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
// out HTLCs.
if isOurs {
commitTx, err = CreateCommitTx(
fundingTxIn(cb.chanState), keyRing, &cb.chanState.LocalChanCfg,
&cb.chanState.RemoteChanCfg, ourBalance.ToSatoshis(),
theirBalance.ToSatoshis(),
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
&cb.chanState.LocalChanCfg, &cb.chanState.RemoteChanCfg,
ourBalance.ToSatoshis(), theirBalance.ToSatoshis(),
)
} else {
commitTx, err = CreateCommitTx(
fundingTxIn(cb.chanState), keyRing, &cb.chanState.RemoteChanCfg,
&cb.chanState.LocalChanCfg, theirBalance.ToSatoshis(),
ourBalance.ToSatoshis(),
cb.chanState.ChanType, fundingTxIn(cb.chanState), keyRing,
&cb.chanState.RemoteChanCfg, &cb.chanState.LocalChanCfg,
theirBalance.ToSatoshis(), ourBalance.ToSatoshis(),
)
}
if err != nil {
@ -389,7 +420,8 @@ func (cb *CommitmentBuilder) createUnsignedCommitmentTx(ourBalance,
// 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..
func CreateCommitTx(fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
func CreateCommitTx(chanType channeldb.ChannelType,
fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
amountToLocal, amountToRemote btcutil.Amount) (*wire.MsgTx, error) {
@ -412,10 +444,9 @@ func CreateCommitTx(fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
return nil, err
}
// Next, we create the script paying to the remote. This is just a
// regular P2WPKH output, without any added CSV delay.
toRemoteWitnessKeyHash, err := input.CommitScriptUnencumbered(
keyRing.ToRemoteKey,
// Next, we create the script paying to the remote.
toRemoteScript, err := CommitScriptToRemote(
chanType, uint32(remoteChanCfg.CsvDelay), keyRing.ToRemoteKey,
)
if err != nil {
return nil, err
@ -436,7 +467,7 @@ func CreateCommitTx(fundingOutput wire.TxIn, keyRing *CommitmentKeyRing,
}
if amountToRemote >= localChanCfg.DustLimit {
commitTx.AddTxOut(&wire.TxOut{
PkScript: toRemoteWitnessKeyHash,
PkScript: toRemoteScript.PkScript,
Value: int64(amountToRemote),
})
}

@ -1048,8 +1048,10 @@ func testSpendValidation(t *testing.T, tweakless bool) {
// our commitments, if it's tweakless, his key will just be his regular
// pubkey.
bobPayKey := input.TweakPubKey(bobKeyPub, commitPoint)
channelType := channeldb.SingleFunderBit
if tweakless {
bobPayKey = bobKeyPub
channelType = channeldb.SingleFunderTweaklessBit
}
aliceCommitTweak := input.SingleTweakBytes(commitPoint, aliceKeyPub)
@ -1086,8 +1088,8 @@ func testSpendValidation(t *testing.T, tweakless bool) {
ToRemoteKey: bobPayKey,
}
commitmentTx, err := CreateCommitTx(
*fakeFundingTxIn, keyRing, aliceChanCfg, bobChanCfg,
channelBalance, channelBalance,
channelType, *fakeFundingTxIn, keyRing, aliceChanCfg,
bobChanCfg, channelBalance, channelBalance,
)
if err != nil {
t.Fatalf("unable to create commitment transaction: %v", nil)

@ -783,8 +783,8 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
)
ourCommitTx, err := CreateCommitTx(
fundingTxIn, localCommitmentKeys, ourChanCfg, theirChanCfg,
localBalance, remoteBalance,
chanType, fundingTxIn, localCommitmentKeys, ourChanCfg,
theirChanCfg, localBalance, remoteBalance,
)
if err != nil {
return nil, nil, err
@ -796,8 +796,8 @@ func CreateCommitmentTxns(localBalance, remoteBalance btcutil.Amount,
}
theirCommitTx, err := CreateCommitTx(
fundingTxIn, remoteCommitmentKeys, theirChanCfg, ourChanCfg,
remoteBalance, localBalance,
chanType, fundingTxIn, remoteCommitmentKeys, theirChanCfg,
ourChanCfg, remoteBalance, localBalance,
)
if err != nil {
return nil, nil, err