lnwallet: populate the output index of an HTLC in ChannelDeltas
This commit modifies the logic within the state machine to properly populate the new field of `OutputIndex` which the HTLC stored within a channel delta. With this change, in the future we’ll be able to quickly locate a particular HTLC output in the scenario that the commitment transaction has been broadcast on-chain and we need to sweep it. Allocating a few extra bytes on-disk saves us from the guess-and-check logic+code required otherwise.
This commit is contained in:
parent
2d884618aa
commit
188811cf05
@ -34,9 +34,9 @@ const (
|
|||||||
// * should be tuned to account for max tx "cost"
|
// * should be tuned to account for max tx "cost"
|
||||||
MaxPendingPayments = 100
|
MaxPendingPayments = 100
|
||||||
|
|
||||||
// InitialRevocationWindow is the number of unrevoked commitment
|
// InitialRevocationWindow is the number of revoked commitment
|
||||||
// transactions allowed within the commitment chain. This value allows
|
// transactions allowed within the commitment chain. This value allows
|
||||||
// a greater degree of desynchronization by allowing either parties to
|
// a greater degree of de-synchronization by allowing either parties to
|
||||||
// extend the other's commitment chain non-interactively, and also
|
// extend the other's commitment chain non-interactively, and also
|
||||||
// serves as a flow control mechanism to a degree.
|
// serves as a flow control mechanism to a degree.
|
||||||
InitialRevocationWindow = 4
|
InitialRevocationWindow = 4
|
||||||
@ -105,7 +105,7 @@ type PaymentDescriptor struct {
|
|||||||
// log by the ParentIndex.
|
// log by the ParentIndex.
|
||||||
RPreimage PaymentHash
|
RPreimage PaymentHash
|
||||||
|
|
||||||
// Timeout is the absolute timeout in blocks, afterwhich this HTLC
|
// Timeout is the absolute timeout in blocks, after which this HTLC
|
||||||
// expires.
|
// expires.
|
||||||
Timeout uint32
|
Timeout uint32
|
||||||
|
|
||||||
@ -147,6 +147,11 @@ type PaymentDescriptor struct {
|
|||||||
// possible upstream peers in the route.
|
// possible upstream peers in the route.
|
||||||
isForwarded bool
|
isForwarded bool
|
||||||
settled bool
|
settled bool
|
||||||
|
|
||||||
|
// pkScript is the raw public key script that encodes the redemption
|
||||||
|
// rules for this particular HTLC. This field will only be populated
|
||||||
|
// iff the EntryType of this PaymentDescriptor is Add.
|
||||||
|
pkScript []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// commitment represents a commitment to a new state within an active channel.
|
// commitment represents a commitment to a new state within an active channel.
|
||||||
@ -205,6 +210,22 @@ func (c *commitment) toChannelDelta() (*channeldb.ChannelDelta, error) {
|
|||||||
Htlcs: make([]*channeldb.HTLC, 0, numHtlcs),
|
Htlcs: make([]*channeldb.HTLC, 0, numHtlcs),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// As we also store the output index of the HTLC for continence
|
||||||
|
// purposes, we create a small helper function to locate the output
|
||||||
|
// index of a particular HTLC within the current commitment
|
||||||
|
// transaction.
|
||||||
|
locateOutputIndex := func(p *PaymentDescriptor) uint16 {
|
||||||
|
var idx uint16
|
||||||
|
for i, txOut := range c.txn.TxOut {
|
||||||
|
// TODO(roasbeef): duplicated payment hashes...
|
||||||
|
if bytes.Equal(txOut.PkScript, p.pkScript) {
|
||||||
|
idx = uint16(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
for _, htlc := range c.outgoingHTLCs {
|
for _, htlc := range c.outgoingHTLCs {
|
||||||
h := &channeldb.HTLC{
|
h := &channeldb.HTLC{
|
||||||
Incoming: false,
|
Incoming: false,
|
||||||
@ -212,6 +233,7 @@ func (c *commitment) toChannelDelta() (*channeldb.ChannelDelta, error) {
|
|||||||
RHash: htlc.RHash,
|
RHash: htlc.RHash,
|
||||||
RefundTimeout: htlc.Timeout,
|
RefundTimeout: htlc.Timeout,
|
||||||
RevocationDelay: 0,
|
RevocationDelay: 0,
|
||||||
|
OutputIndex: locateOutputIndex(htlc),
|
||||||
}
|
}
|
||||||
delta.Htlcs = append(delta.Htlcs, h)
|
delta.Htlcs = append(delta.Htlcs, h)
|
||||||
}
|
}
|
||||||
@ -222,6 +244,7 @@ func (c *commitment) toChannelDelta() (*channeldb.ChannelDelta, error) {
|
|||||||
RHash: htlc.RHash,
|
RHash: htlc.RHash,
|
||||||
RefundTimeout: htlc.Timeout,
|
RefundTimeout: htlc.Timeout,
|
||||||
RevocationDelay: 0,
|
RevocationDelay: 0,
|
||||||
|
OutputIndex: locateOutputIndex(htlc),
|
||||||
}
|
}
|
||||||
delta.Htlcs = append(delta.Htlcs, h)
|
delta.Htlcs = append(delta.Htlcs, h)
|
||||||
}
|
}
|
||||||
@ -336,7 +359,7 @@ type LightningChannel struct {
|
|||||||
// revocationWindowEdge is the edge of the current revocation window.
|
// revocationWindowEdge is the edge of the current revocation window.
|
||||||
// New revocations for prior states created by this channel extend the
|
// New revocations for prior states created by this channel extend the
|
||||||
// edge of this revocation window. The existence of a revocation window
|
// edge of this revocation window. The existence of a revocation window
|
||||||
// allows the remote party to initiate new state updates independantly
|
// allows the remote party to initiate new state updates independently
|
||||||
// until the window is exhausted.
|
// until the window is exhausted.
|
||||||
revocationWindowEdge uint64
|
revocationWindowEdge uint64
|
||||||
|
|
||||||
@ -349,7 +372,7 @@ type LightningChannel struct {
|
|||||||
|
|
||||||
// revocationWindow is a window of revocations sent to use by the
|
// revocationWindow is a window of revocations sent to use by the
|
||||||
// remote party, allowing us to create new commitment transactions
|
// remote party, allowing us to create new commitment transactions
|
||||||
// until depleated. The revocations don't contain a valid pre-iamge,
|
// until depleted. The revocations don't contain a valid pre-image,
|
||||||
// only an additional key/hash allowing us to create a new commitment
|
// only an additional key/hash allowing us to create a new commitment
|
||||||
// transaction for the remote node that they are able to revoke. If
|
// transaction for the remote node that they are able to revoke. If
|
||||||
// this slice is empty, then we cannot make any new updates to their
|
// this slice is empty, then we cannot make any new updates to their
|
||||||
@ -529,7 +552,7 @@ func (lc *LightningChannel) restoreStateLogs() error {
|
|||||||
for _, htlc := range lc.channelState.Htlcs {
|
for _, htlc := range lc.channelState.Htlcs {
|
||||||
// TODO(roasbeef): set isForwarded to false for all? need to
|
// TODO(roasbeef): set isForwarded to false for all? need to
|
||||||
// persist state w.r.t to if forwarded or not, or can
|
// persist state w.r.t to if forwarded or not, or can
|
||||||
// inadvertenly trigger replays
|
// inadvertently trigger replays
|
||||||
pd := &PaymentDescriptor{
|
pd := &PaymentDescriptor{
|
||||||
RHash: htlc.RHash,
|
RHash: htlc.RHash,
|
||||||
Timeout: htlc.RefundTimeout,
|
Timeout: htlc.RefundTimeout,
|
||||||
@ -666,14 +689,16 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, htlc := range filteredHTLCView.ourUpdates {
|
for _, htlc := range filteredHTLCView.ourUpdates {
|
||||||
if err := lc.addHTLC(commitTx, ourCommitTx, htlc,
|
err := lc.addHTLC(commitTx, ourCommitTx, htlc,
|
||||||
revocationHash, delay, false); err != nil {
|
revocationHash, delay, false)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, htlc := range filteredHTLCView.theirUpdates {
|
for _, htlc := range filteredHTLCView.theirUpdates {
|
||||||
if err := lc.addHTLC(commitTx, ourCommitTx, htlc,
|
err := lc.addHTLC(commitTx, ourCommitTx, htlc,
|
||||||
revocationHash, delay, true); err != nil {
|
revocationHash, delay, true)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -687,7 +712,7 @@ func (lc *LightningChannel) fetchCommitmentView(remoteChain bool,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the transactions according to the agreed upon cannonical
|
// Sort the transactions according to the agreed upon canonical
|
||||||
// ordering. This lets us skip sending the entire transaction over,
|
// ordering. This lets us skip sending the entire transaction over,
|
||||||
// instead we'll just send signatures.
|
// instead we'll just send signatures.
|
||||||
txsort.InPlaceSort(commitTx)
|
txsort.InPlaceSort(commitTx)
|
||||||
@ -1407,7 +1432,10 @@ func (lc *LightningChannel) ChannelPoint() *wire.OutPoint {
|
|||||||
// addHTLC adds a new HTLC to the passed commitment transaction. One of four
|
// 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
|
// 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
|
// is incoming and if it's being applied to our commitment transaction or that
|
||||||
// of the remote node's.
|
// 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,
|
func (lc *LightningChannel) addHTLC(commitTx *wire.MsgTx, ourCommit bool,
|
||||||
paymentDesc *PaymentDescriptor, revocation [32]byte, delay uint32,
|
paymentDesc *PaymentDescriptor, revocation [32]byte, delay uint32,
|
||||||
isIncoming bool) error {
|
isIncoming bool) error {
|
||||||
@ -1463,6 +1491,10 @@ func (lc *LightningChannel) addHTLC(commitTx *wire.MsgTx, ourCommit bool,
|
|||||||
amountPending := int64(paymentDesc.Amount)
|
amountPending := int64(paymentDesc.Amount)
|
||||||
commitTx.AddTxOut(wire.NewTxOut(amountPending, htlcP2WSH))
|
commitTx.AddTxOut(wire.NewTxOut(amountPending, htlcP2WSH))
|
||||||
|
|
||||||
|
// Store the pkScript of this particular PaymentDescriptor so we can
|
||||||
|
// quickly locate it within the commitment transaction later.
|
||||||
|
paymentDesc.pkScript = htlcP2WSH
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1470,7 +1502,7 @@ func (lc *LightningChannel) addHTLC(commitTx *wire.MsgTx, ourCommit bool,
|
|||||||
// locked-down to initiate a force closure by broadcasting the latest state
|
// locked-down to initiate a force closure by broadcasting the latest state
|
||||||
// on-chain. The summary includes all the information required to claim all
|
// on-chain. The summary includes all the information required to claim all
|
||||||
// rightfully owned outputs.
|
// rightfully owned outputs.
|
||||||
// TODO(roasbeef): generalize, add HTLC info, revocatio info, etc.
|
// TODO(roasbeef): generalize, add HTLC info, etc.
|
||||||
type ForceCloseSummary struct {
|
type ForceCloseSummary struct {
|
||||||
// CloseTx is the transaction which closed the channel on-chain. If we
|
// CloseTx is the transaction which closed the channel on-chain. If we
|
||||||
// initiate the force close, then this'll be our latest commitment
|
// initiate the force close, then this'll be our latest commitment
|
||||||
@ -1487,7 +1519,7 @@ type ForceCloseSummary struct {
|
|||||||
SelfOutputMaturity uint32
|
SelfOutputMaturity uint32
|
||||||
|
|
||||||
// SelfOutputSignDesc is a fully populated sign descriptor capable of
|
// SelfOutputSignDesc is a fully populated sign descriptor capable of
|
||||||
// generating a valid signature to swee the self output.
|
// generating a valid signature to sweep the self output.
|
||||||
SelfOutputSignDesc *SignDescriptor
|
SelfOutputSignDesc *SignDescriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1566,7 +1598,7 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// With the necessary information gatehred above, create a new sign
|
// With the necessary information gathered above, create a new sign
|
||||||
// descriptor which is capable of generating the signature the caller
|
// descriptor which is capable of generating the signature the caller
|
||||||
// needs to sweep this output. The hash cache, and input index are not
|
// needs to sweep this output. The hash cache, and input index are not
|
||||||
// set as the caller will decide these values once sweeping the output.
|
// set as the caller will decide these values once sweeping the output.
|
||||||
@ -1599,15 +1631,16 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
|
|||||||
|
|
||||||
// InitCooperativeClose initiates a cooperative closure of an active lightning
|
// InitCooperativeClose initiates a cooperative closure of an active lightning
|
||||||
// channel. This method should only be executed once all pending HTLCs (if any)
|
// channel. This method should only be executed once all pending HTLCs (if any)
|
||||||
// on the channel have been cleared/removed. Upon completion, the source channel
|
// on the channel have been cleared/removed. Upon completion, the source
|
||||||
// will shift into the "closing" state, which indicates that all incoming/outgoing
|
// channel will shift into the "closing" state, which indicates that all
|
||||||
// HTLC requests should be rejected. A signature for the closing transaction,
|
// incoming/outgoing HTLC requests should be rejected. A signature for the
|
||||||
// and the txid of the closing transaction are returned. The initiator of the
|
// closing transaction, and the txid of the closing transaction are returned.
|
||||||
// channel closure should then watch the blockchain for a confirmation of the
|
// The initiator of the channel closure should then watch the blockchain for a
|
||||||
// closing transaction before considering the channel terminated. In the case
|
// confirmation of the closing transaction before considering the channel
|
||||||
// of an unresponsive remote party, the initiator can either choose to execute
|
// terminated. In the case of an unresponsive remote party, the initiator can
|
||||||
// a force closure, or backoff for a period of time, and retry the cooperative
|
// either choose to execute a force closure, or backoff for a period of time,
|
||||||
// closure.
|
// and retry the cooperative closure.
|
||||||
|
//
|
||||||
// TODO(roasbeef): caller should initiate signal to reject all incoming HTLCs,
|
// TODO(roasbeef): caller should initiate signal to reject all incoming HTLCs,
|
||||||
// settle any inflight.
|
// settle any inflight.
|
||||||
func (lc *LightningChannel) InitCooperativeClose() ([]byte, *wire.ShaHash, error) {
|
func (lc *LightningChannel) InitCooperativeClose() ([]byte, *wire.ShaHash, error) {
|
||||||
@ -1646,11 +1679,11 @@ func (lc *LightningChannel) InitCooperativeClose() ([]byte, *wire.ShaHash, error
|
|||||||
|
|
||||||
// CompleteCooperativeClose completes the cooperative closure of the target
|
// CompleteCooperativeClose completes the cooperative closure of the target
|
||||||
// active lightning channel. This method should be called in response to the
|
// active lightning channel. This method should be called in response to the
|
||||||
// remote node initating a cooperative channel closure. A fully signed closure
|
// remote node initiating a cooperative channel closure. A fully signed closure
|
||||||
// transaction is returned. It is the duty of the responding node to broadcast
|
// transaction is returned. It is the duty of the responding node to broadcast
|
||||||
// a signed+valid closure transaction to the network.
|
// a signed+valid closure transaction to the network.
|
||||||
//
|
//
|
||||||
// NOTE: The passed remote sig is expected to the a fully complete signature
|
// NOTE: The passed remote sig is expected to be a fully complete signature
|
||||||
// including the proper sighash byte.
|
// including the proper sighash byte.
|
||||||
func (lc *LightningChannel) CompleteCooperativeClose(remoteSig []byte) (*wire.MsgTx, error) {
|
func (lc *LightningChannel) CompleteCooperativeClose(remoteSig []byte) (*wire.MsgTx, error) {
|
||||||
lc.Lock()
|
lc.Lock()
|
||||||
|
Loading…
Reference in New Issue
Block a user