lnwallet: update Channel[Reservation+Contribution] for new funding flow

This commit updates the channel reservation workflow in order to
properly implement the new funding workflow defined in BOLT-0002.

The workflow itself hasn’t changed significantly, but the contents of
the contributions of both sides have. The bulk of the fields within the
contribution of both sides has been boiled down into a pointer to the
ChannelConfig which houses all the data required to handle all states
of the channel, and commitment state machine.

For the two portions which are dictated by the other party, we now add
builder-like modifiers to allow specifying the constraints after the
initial portion of the workflow.
This commit is contained in:
Olaoluwa Osuntokun 2017-07-29 18:58:55 -07:00
parent e930af4b43
commit c63c7cd22f
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2

@ -6,16 +6,17 @@ import (
"github.com/lightningnetwork/lnd/channeldb"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
)
// ChannelContribution is the primary constituent of the funding workflow within
// lnwallet. Each side first exchanges their respective contributions along with
// channel specific parameters like the min fee/KB. Once contributions have been
// exchanged, each side will then produce signatures for all their inputs to the
// funding transactions, and finally a signature for the other party's version
// of the commitment transaction.
// ChannelContribution is the primary constituent of the funding workflow
// within lnwallet. Each side first exchanges their respective contributions
// along with channel specific parameters like the min fee/KB. Once
// contributions have been exchanged, each side will then produce signatures
// for all their inputs to the funding transactions, and finally a signature
// for the other party's version of the commitment transaction.
type ChannelContribution struct {
// FundingOutpoint is the amount of funds contributed to the funding
// transaction.
@ -29,31 +30,22 @@ type ChannelContribution struct {
// channel capacity.
ChangeOutputs []*wire.TxOut
// MultiSigKey is the the key to be used for the funding transaction's
// P2SH multi-sig 2-of-2 output.
// TODO(roasbeef): replace with CDP
MultiSigKey *btcec.PublicKey
// FirstCommitmentPoint is the first commitment point that will be used
// to create the revocation key in the first commitment transaction we
// send to the remote party.
FirstCommitmentPoint *btcec.PublicKey
// CommitKey is the key to be used for this party's version of the
// commitment transaction.
CommitKey *btcec.PublicKey
// ChannelConfig is the concrete contribution that this node is
// offering to the channel. This includes all the various constraints
// such as the min HTLC, and also all the keys which will be used for
// the duration of the channel.
*channeldb.ChannelConfig
}
// DeliveryAddress is the address to be used for delivery of cleared
// channel funds in the scenario of a cooperative channel closure.
DeliveryAddress btcutil.Address
// RevocationKey is the key to be used in the revocation clause for the
// initial version of this party's commitment transaction.
RevocationKey *btcec.PublicKey
// DustLimit is the threshold below which no HTLC output should be
// generated for this party. HTLCs below this amount are not
// enforceable on-chain from this party's point of view.
DustLimit btcutil.Amount
// CsvDelay The delay (in blocks) to be used for the pay-to-self output
// in this party's version of the commitment transaction.
CsvDelay uint32
// toChanConfig returns the raw channel configuration generated by a node's
// contribution to the channel.
func (c *ChannelContribution) toChanConfig() channeldb.ChannelConfig {
return *c.ChannelConfig
}
// InputScript represents any script inputs required to redeem a previous
@ -65,8 +57,8 @@ type InputScript struct {
}
// ChannelReservation represents an intent to open a lightning payment channel
// a counterparty. The funding processes from reservation to channel opening
// is a 3-step process. In order to allow for full concurrency during the
// a counterparty. The funding processes from reservation to channel opening is
// a 3-step process. In order to allow for full concurrency during the
// reservation workflow, resources consumed by a contribution are "locked"
// themselves. This prevents a number of race conditions such as two funding
// transactions double-spending the same input. A reservation can also be
@ -122,10 +114,6 @@ type ChannelReservation struct {
// throughout its lifetime.
reservationID uint64
// numConfsToOpen is the number of confirmations required before the
// channel should be considered open.
numConfsToOpen uint16
// pushSat the amount of satoshis that should be pushed to the
// responder of a single funding channel as part of the initial
// commitment state.
@ -145,9 +133,9 @@ type ChannelReservation struct {
// used only internally by lnwallet. In order to concurrent safety, the
// creation of all channel reservations should be carried out via the
// lnwallet.InitChannelReservation interface.
func NewChannelReservation(capacity, fundingAmt btcutil.Amount,
feePerKw btcutil.Amount, wallet *LightningWallet, id uint64,
numConfs uint16, pushSat btcutil.Amount) *ChannelReservation {
func NewChannelReservation(capacity, fundingAmt, feePerKw btcutil.Amount,
wallet *LightningWallet, id uint64, pushSat btcutil.Amount,
chainHash *chainhash.Hash) *ChannelReservation {
var (
ourBalance btcutil.Amount
@ -206,38 +194,66 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount,
return &ChannelReservation{
ourContribution: &ChannelContribution{
FundingAmount: ourBalance,
ChannelConfig: &channeldb.ChannelConfig{},
},
theirContribution: &ChannelContribution{
FundingAmount: theirBalance,
ChannelConfig: &channeldb.ChannelConfig{},
},
partialState: &channeldb.OpenChannel{
Capacity: capacity,
IsInitiator: initiator,
IsPending: true,
ChanType: chanType,
OurBalance: ourBalance,
TheirBalance: theirBalance,
FeePerKw: feePerKw,
Db: wallet.ChannelDB,
CommitFee: commitFee,
ChainHash: *chainHash,
Db: wallet.Cfg.Database,
Capacity: capacity,
IsInitiator: initiator,
IsPending: true,
ChanType: chanType,
LocalBalance: ourBalance,
RemoteBalance: theirBalance,
FeePerKw: feePerKw,
CommitFee: commitFee,
},
numConfsToOpen: numConfs,
pushSat: pushSat,
reservationID: id,
chanOpen: make(chan *openChanDetails, 1),
chanOpenErr: make(chan error, 1),
wallet: wallet,
pushSat: pushSat,
reservationID: id,
chanOpen: make(chan *openChanDetails, 1),
chanOpenErr: make(chan error, 1),
wallet: wallet,
}
}
// SetNumConfsRequired sets the number of confirmations that are required for
// the ultimate funding transaction before the channel can be considered open.
// This is distinct from the main reservation workflow as it allows
// implementations a bit more flexibility w.r.t to if the responder of the
// initiator sets decides the number of confirmations needed.
func (r *ChannelReservation) SetNumConfsRequired(numConfs uint16) {
r.Lock()
defer r.Unlock()
r.partialState.NumConfsRequired = numConfs
}
// RequireLocalDelay sets the mandatory CSV delay that MUST be used when
// creating the local commitment transaction. This is distinct from the normal
// reservation workflow as the remote party will dictate this value for us.
//
// TODO(roasbeef): abstract out dictation of params remote side gives
func (r *ChannelReservation) RequireLocalDelay(csvDelay uint16) {
r.Lock()
defer r.Unlock()
r.ourContribution.ChannelConfig.CsvDelay = csvDelay
}
// OurContribution returns the wallet's fully populated contribution to the
// pending payment channel. See 'ChannelContribution' for further details
// regarding the contents of a contribution.
//
// NOTE: This SHOULD NOT be modified.
// TODO(roasbeef): make copy?
func (r *ChannelReservation) OurContribution() *ChannelContribution {
r.RLock()
defer r.RUnlock()
return r.ourContribution
}
@ -277,9 +293,10 @@ func (r *ChannelReservation) ProcessSingleContribution(theirContribution *Channe
}
// TheirContribution returns the counterparty's pending contribution to the
// payment channel. See 'ChannelContribution' for further details regarding
// the contents of a contribution. This attribute will ONLY be available
// after a call to .ProcesContribution().
// payment channel. See 'ChannelContribution' for further details regarding the
// contents of a contribution. This attribute will ONLY be available after a
// call to .ProcesContribution().
//
// NOTE: This SHOULD NOT be modified.
func (r *ChannelReservation) TheirContribution() *ChannelContribution {
r.RLock()
@ -292,6 +309,7 @@ func (r *ChannelReservation) TheirContribution() *ChannelContribution {
// version of the commitment transaction. The signatures for the wallet's
// inputs to the funding transaction are returned in sorted order according to
// BIP-69: https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
//
// NOTE: These signatures will only be populated after a call to
// .ProcesContribution()
func (r *ChannelReservation) OurSignatures() ([]*InputScript, []byte) {
@ -300,18 +318,19 @@ func (r *ChannelReservation) OurSignatures() ([]*InputScript, []byte) {
return r.ourFundingInputScripts, r.ourCommitmentSig
}
// CompleteReservation finalizes the pending channel reservation,
// transitioning from a pending payment channel, to an open payment
// channel. All passed signatures to the counterparty's inputs to the funding
// transaction will be fully verified. Signatures are expected to be passed in
// sorted order according to BIP-69:
// https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki. Additionally,
// verification is performed in order to ensure that the counterparty supplied
// a valid signature to our version of the commitment transaction.
// Once this method returns, caller's should then call .WaitForChannelOpen()
// which will block until the funding transaction obtains the configured number
// of confirmations. Once the method unblocks, a LightningChannel instance is
// returned, marking the channel available for updates.
// CompleteReservation finalizes the pending channel reservation, transitioning
// from a pending payment channel, to an open payment channel. All passed
// signatures to the counterparty's inputs to the funding transaction will be
// fully verified. Signatures are expected to be passed in sorted order
// according to BIP-69:
// https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki.
// Additionally, verification is performed in order to ensure that the
// counterparty supplied a valid signature to our version of the commitment
// transaction. Once this method returns, caller's should then call
// .WaitForChannelOpen() which will block until the funding transaction obtains
// the configured number of confirmations. Once the method unblocks, a
// LightningChannel instance is returned, marking the channel available for
// updates.
func (r *ChannelReservation) CompleteReservation(fundingInputScripts []*InputScript,
commitmentSig []byte) (*channeldb.OpenChannel, error) {
@ -331,27 +350,24 @@ func (r *ChannelReservation) CompleteReservation(fundingInputScripts []*InputScr
}
// CompleteReservationSingle finalizes the pending single funder channel
// reservation. Using the funding outpoint of the constructed funding transaction,
// and the initiator's signature for our version of the commitment transaction,
// we are able to verify the correctness of our committment transaction as
// crafted by the initiator. Once this method returns, our signature for the
// initiator's version of the commitment transaction is available via
// the .OurSignatures() method. As this method should only be called as a
// response to a single funder channel, only a commitment signature will be
// populated.
func (r *ChannelReservation) CompleteReservationSingle(
revocationKey *btcec.PublicKey, fundingPoint *wire.OutPoint,
commitSig []byte, obsfucator [StateHintSize]byte) (*channeldb.OpenChannel, error) {
// reservation. Using the funding outpoint of the constructed funding
// transaction, and the initiator's signature for our version of the commitment
// transaction, we are able to verify the correctness of our commitment
// transaction as crafted by the initiator. Once this method returns, our
// signature for the initiator's version of the commitment transaction is
// available via the .OurSignatures() method. As this method should only be
// called as a response to a single funder channel, only a commitment signature
// will be populated.
func (r *ChannelReservation) CompleteReservationSingle(fundingPoint *wire.OutPoint,
commitSig []byte) (*channeldb.OpenChannel, error) {
errChan := make(chan error, 1)
completeChan := make(chan *channeldb.OpenChannel, 1)
r.wallet.msgChan <- &addSingleFunderSigsMsg{
pendingFundingID: r.reservationID,
revokeKey: revocationKey,
fundingOutpoint: fundingPoint,
theirCommitmentSig: commitSig,
obsfucator: obsfucator,
completeChan: completeChan,
err: errChan,
}
@ -363,6 +379,7 @@ func (r *ChannelReservation) CompleteReservationSingle(
// funding transaction belonging to them, as well as their signature for the
// wallet's version of the commitment transaction. This methods is provided for
// additional verification, such as needed by tests.
//
// NOTE: These attributes will be unpopulated before a call to
// .CompleteReservation().
func (r *ChannelReservation) TheirSignatures() ([]*InputScript, []byte) {
@ -383,34 +400,6 @@ func (r *ChannelReservation) FinalFundingTx() *wire.MsgTx {
return r.fundingTx
}
// FundingRedeemScript returns the fully populated funding redeem script.
//
// NOTE: This method will only return a non-nil value after either
// ProcesContribution or ProcessSingleContribution have been executed and
// returned without error.
func (r *ChannelReservation) FundingRedeemScript() []byte {
r.RLock()
defer r.RUnlock()
return r.partialState.FundingWitnessScript
}
// LocalCommitTx returns the commitment transaction for the local node involved
// in this funding reservation.
func (r *ChannelReservation) LocalCommitTx() *wire.MsgTx {
r.RLock()
defer r.RUnlock()
return r.partialState.OurCommitTx
}
// SetTheirDustLimit set dust limit of the remote party.
func (r *ChannelReservation) SetTheirDustLimit(dustLimit btcutil.Amount) {
r.Lock()
defer r.Unlock()
r.partialState.TheirDustLimit = dustLimit
}
// FundingOutpoint returns the outpoint of the funding transaction.
//
// NOTE: The pointer returned will only be set once the .ProcesContribution()
@ -420,20 +409,7 @@ func (r *ChannelReservation) SetTheirDustLimit(dustLimit btcutil.Amount) {
func (r *ChannelReservation) FundingOutpoint() *wire.OutPoint {
r.RLock()
defer r.RUnlock()
return r.partialState.FundingOutpoint
}
// StateNumObfuscator returns the bytes to be used to obsfucate the state
// number hints for all future states of the commitment transaction for this
// workflow.
//
// NOTE: This value will only be available for a single funder workflow after
// the CompleteReservation or CompleteReservationSingle methods have been
// successfully executed.
func (r *ChannelReservation) StateNumObfuscator() [StateHintSize]byte {
r.RLock()
defer r.RUnlock()
return r.partialState.StateHintObsfucator
return &r.partialState.FundingOutpoint
}
// Cancel abandons this channel reservation. This method should be called in
@ -459,8 +435,8 @@ type OpenChannelDetails struct {
// ChannelReservation and the required funding workflow.
Channel *LightningChannel
// ConfirmationHeight is the block height within the chain that included
// the channel.
// ConfirmationHeight is the block height within the chain that
// included the channel.
ConfirmationHeight uint32
// TransactionIndex is the index within the confirming block that the