From 17d29ba62e92643654d06dad8884109dcbbacbbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20J=C3=A4mthagen?= Date: Sat, 21 Jan 2017 11:54:37 +0100 Subject: [PATCH] lnwallet: properly restore public key scripts for HTLCs Create a new helper method called genHtlcScript which will generate the public key scripts for a supplied HTLC. This functionality from addHTLC is removed, and addHTLC will instead call this new method. In restoreStateLogs we will regenerate the public key scripts for the HTLCs with genHtlcScript and restore the proper values. --- lnwallet/channel.go | 104 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 24 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 1f051ccb..2d142572 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1017,7 +1017,7 @@ func (lc *LightningChannel) Stop() { // restoreStateLogs runs through the current locked-in HTLCs from the point of // view of the channel and insert corresponding log entries (both local and -// remote) for each HTLC read from disk. This method is required sync the +// remote) for each HTLC read from disk. This method is required to sync the // in-memory state of the state machine with that read from persistent storage. func (lc *LightningChannel) restoreStateLogs() error { var pastHeight uint64 @@ -1025,20 +1025,63 @@ func (lc *LightningChannel) restoreStateLogs() error { pastHeight = lc.currentHeight - 1 } + localKey := lc.channelState.OurCommitKey + remoteKey := lc.channelState.TheirCommitKey + ourDelay := lc.channelState.LocalCsvDelay + theirDelay := lc.channelState.RemoteCsvDelay + + ourRevPreImage, err := lc.channelState.RevocationProducer.AtIndex(lc.currentHeight) + if err != nil { + return err + } + ourRevocation := sha256.Sum256(ourRevPreImage[:]) + + theirRevocation := lc.channelState.TheirCurrentRevocationHash + var ourCounter, theirCounter uint64 + for _, htlc := range lc.channelState.Htlcs { // TODO(roasbeef): set isForwarded to false for all? need to // persist state w.r.t to if forwarded or not, or can // inadvertently trigger replays + + // The proper pkScripts for this PaymentDescriptor must be + // generated. + var ourP2WSH, theirP2WSH []byte + timeout := htlc.RefundTimeout + rHash := htlc.RHash + amt := htlc.Amt + isDustLocal := amt < lc.channelState.OurDustLimit + isDustRemote := amt < lc.channelState.TheirDustLimit + + // It is unnecessary to generate pkScripts for dust outputs. + if !isDustLocal { + ourP2WSH, err = lc.genHtlcScript(htlc.Incoming, true, timeout, + ourDelay, remoteKey, localKey, ourRevocation, rHash) + if err != nil { + return err + } + } + // It is unnecessary to generate pkScripts for dust outputs. + if !isDustRemote { + theirP2WSH, err = lc.genHtlcScript(htlc.Incoming, false, timeout, + theirDelay, remoteKey, localKey, theirRevocation, rHash) + if err != nil { + return err + } + } + pd := &PaymentDescriptor{ - RHash: htlc.RHash, - Timeout: htlc.RefundTimeout, - Amount: htlc.Amt, + RHash: rHash, + Timeout: timeout, + Amount: amt, EntryType: Add, addCommitHeightRemote: pastHeight, addCommitHeightLocal: pastHeight, - isDustLocal: htlc.Amt < lc.channelState.OurDustLimit, - isDustRemote: htlc.Amt < lc.channelState.TheirDustLimit, + isDustLocal: isDustLocal, + isDustRemote: isDustRemote, + ourPkScript: ourP2WSH, + theirPkScript: theirP2WSH, } if !htlc.Incoming { @@ -2097,22 +2140,12 @@ func (lc *LightningChannel) ChannelPoint() *wire.OutPoint { return lc.channelState.ChanID } -// addHTLC adds a new HTLC to the passed commitment transaction. One of four -// full scripts will be generated for the HTLC output depending on if the HTLC -// is incoming and if it's being applied to our commitment transaction or that -// of the remote node's. Additionally, in order to be able to efficiently -// locate the added HTLC on the commitment transaction from the -// PaymentDescriptor that generated it, the generated script is stored within -// the descriptor itself. -func (lc *LightningChannel) addHTLC(commitTx *wire.MsgTx, ourCommit bool, - paymentDesc *PaymentDescriptor, revocation [32]byte, delay uint32, - isIncoming bool) error { - - localKey := lc.channelState.OurCommitKey - remoteKey := lc.channelState.TheirCommitKey - timeout := paymentDesc.Timeout - rHash := paymentDesc.RHash - +// genHtlcScript generates the proper P2WSH public key scripts for the +// HTLC output modified by two-bits denoting if this is an incoming HTLC, and +// if the HTLC is being applied to their commitment transaction or ours. +func (lc *LightningChannel) genHtlcScript(isIncoming, ourCommit bool, + timeout, delay uint32, remoteKey, localKey *btcec.PublicKey, revocation, + rHash [32]byte) ([]byte, error) { // Generate the proper redeem scripts for the HTLC output modified by // two-bits denoting if this is an incoming HTLC, and if the HTLC is // being applied to their commitment transaction or ours. @@ -2145,12 +2178,35 @@ func (lc *LightningChannel) addHTLC(commitTx *wire.MsgTx, ourCommit bool, remoteKey, revocation[:], rHash[:]) } if err != nil { - return err + return nil, err } - // Now that we have the redeem scripts, create the P2WSH public key // script for the output itself. htlcP2WSH, err := witnessScriptHash(pkScript) + if err != nil { + return nil, err + } + return htlcP2WSH, nil +} + +// addHTLC adds a new HTLC to the passed commitment transaction. One of four +// full scripts will be generated for the HTLC output depending on if the HTLC +// is incoming and if it's being applied to our commitment transaction or that +// of the remote node's. Additionally, in order to be able to efficiently +// locate the added HTLC on the commitment transaction from the +// PaymentDescriptor that generated it, the generated script is stored within +// the descriptor itself. +func (lc *LightningChannel) addHTLC(commitTx *wire.MsgTx, ourCommit bool, + paymentDesc *PaymentDescriptor, revocation [32]byte, delay uint32, + isIncoming bool) error { + + localKey := lc.channelState.OurCommitKey + remoteKey := lc.channelState.TheirCommitKey + timeout := paymentDesc.Timeout + rHash := paymentDesc.RHash + + htlcP2WSH, err := lc.genHtlcScript(isIncoming, ourCommit, timeout, + delay, remoteKey, localKey, revocation, rHash) if err != nil { return err }