funding: update funding workflow to adhere to BOLT-0002
This commit updates the main single-funder funding workflow within the fundingManager (initiated via the rpcserver or by a message from a connected peer) to fully adhere to the funding protocol outlined in BOLT-0002. The major changes are as follows: * All messages modified to use the new funding messages in BOLT-0002. * The initiator of a funding workflow no longer decides how many confirmations must elapse before the channel can be considered open. * Rather than each side specifying their desired CSV delay, both sides now specify the CSV delay for the _other_ party.
This commit is contained in:
parent
216cc3f919
commit
07212588fc
@ -18,7 +18,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/txscript"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
"github.com/roasbeef/btcd/wire"
|
||||
"github.com/roasbeef/btcutil"
|
||||
"google.golang.org/grpc"
|
||||
@ -27,6 +27,8 @@ import (
|
||||
const (
|
||||
// TODO(roasbeef): tune
|
||||
msgBufferSize = 50
|
||||
|
||||
defaultCsvDelay = 4
|
||||
)
|
||||
|
||||
// reservationWithCtx encapsulates a pending channel reservation. This wrapper
|
||||
@ -35,12 +37,15 @@ const (
|
||||
// peer. Additionally, this struct houses a response and error channel which is
|
||||
// used to respond to the caller in the case a channel workflow is initiated
|
||||
// via a local signal such as RPC.
|
||||
//
|
||||
// TODO(roasbeef): actually use the context package
|
||||
// * deadlines, etc.
|
||||
type reservationWithCtx struct {
|
||||
reservation *lnwallet.ChannelReservation
|
||||
peerAddress *lnwire.NetAddress
|
||||
|
||||
chanAmt btcutil.Amount
|
||||
|
||||
updates chan *lnrpc.OpenStatusUpdate
|
||||
err chan error
|
||||
}
|
||||
@ -55,35 +60,35 @@ type initFundingMsg struct {
|
||||
*openChanReq
|
||||
}
|
||||
|
||||
// fundingRequestMsg couples an lnwire.SingleFundingRequest message with the
|
||||
// peer who sent the message. This allows the funding manager to queue a
|
||||
// response directly to the peer, progressing the funding workflow.
|
||||
type fundingRequestMsg struct {
|
||||
msg *lnwire.SingleFundingRequest
|
||||
// fundingOpenMsg couples an lnwire.OpenChannel message with the peer who sent
|
||||
// the message. This allows the funding manager to queue a response directly to
|
||||
// the peer, progressing the funding workflow.
|
||||
type fundingOpenMsg struct {
|
||||
msg *lnwire.OpenChannel
|
||||
peerAddress *lnwire.NetAddress
|
||||
}
|
||||
|
||||
// fundingResponseMsg couples an lnwire.SingleFundingResponse message with the
|
||||
// peer who sent the message. This allows the funding manager to queue a
|
||||
// response directly to the peer, progressing the funding workflow.
|
||||
type fundingResponseMsg struct {
|
||||
msg *lnwire.SingleFundingResponse
|
||||
// fundingAcceptMsg couples an lnwire.AcceptChannel message with the peer who
|
||||
// sent the message. This allows the funding manager to queue a response
|
||||
// directly to the peer, progressing the funding workflow.
|
||||
type fundingAcceptMsg struct {
|
||||
msg *lnwire.AcceptChannel
|
||||
peerAddress *lnwire.NetAddress
|
||||
}
|
||||
|
||||
// fundingCompleteMsg couples an lnwire.SingleFundingComplete message with the
|
||||
// peer who sent the message. This allows the funding manager to queue a
|
||||
// response directly to the peer, progressing the funding workflow.
|
||||
type fundingCompleteMsg struct {
|
||||
msg *lnwire.SingleFundingComplete
|
||||
// fundingCreatedMsg couples an lnwire.FundingCreated message with the peer who
|
||||
// sent the message. This allows the funding manager to queue a response
|
||||
// directly to the peer, progressing the funding workflow.
|
||||
type fundingCreatedMsg struct {
|
||||
msg *lnwire.FundingCreated
|
||||
peerAddress *lnwire.NetAddress
|
||||
}
|
||||
|
||||
// fundingSignCompleteMsg couples an lnwire.SingleFundingSignComplete message
|
||||
// with the peer who sent the message. This allows the funding manager to
|
||||
// queue a response directly to the peer, progressing the funding workflow.
|
||||
type fundingSignCompleteMsg struct {
|
||||
msg *lnwire.SingleFundingSignComplete
|
||||
// fundingSignedMsg couples an lnwire.FundingSigned message with the peer who
|
||||
// sent the message. This allows the funding manager to queue a response
|
||||
// directly to the peer, progressing the funding workflow.
|
||||
type fundingSignedMsg struct {
|
||||
msg *lnwire.FundingSigned
|
||||
peerAddress *lnwire.NetAddress
|
||||
}
|
||||
|
||||
@ -103,7 +108,8 @@ type fundingErrorMsg struct {
|
||||
}
|
||||
|
||||
// pendingChannels is a map instantiated per-peer which tracks all active
|
||||
// pending single funded channels indexed by their pending channel identifier.
|
||||
// pending single funded channels indexed by their pending channel identifier,
|
||||
// which is a set of 32-bytes generated via a CSPRNG.
|
||||
type pendingChannels map[[32]byte]*reservationWithCtx
|
||||
|
||||
// serializedPubKey is used within the FundingManager's activeReservations list
|
||||
@ -137,7 +143,7 @@ type fundingConfig struct {
|
||||
|
||||
// ArbiterChan allows the FundingManager to notify the BreachArbiter
|
||||
// that a new channel has been created that should be observed to
|
||||
// ensure that the channel counterparty hasn't broadcasted an invalid
|
||||
// ensure that the channel counterparty hasn't broadcast an invalid
|
||||
// commitment transaction.
|
||||
ArbiterChan chan<- *lnwallet.LightningChannel
|
||||
|
||||
@ -150,6 +156,9 @@ type fundingConfig struct {
|
||||
// actual digest signed is the double sha-256 of the message. In the
|
||||
// case that the private key corresponding to the passed public key
|
||||
// cannot be located, then an error is returned.
|
||||
//
|
||||
// TODO(roasbeef): should instead pass on this responsibility to a
|
||||
// distinct sub-system?
|
||||
SignMessage func(pubKey *btcec.PublicKey, msg []byte) (*btcec.Signature, error)
|
||||
|
||||
// SendAnnouncement is used by the FundingManager to announce newly
|
||||
@ -177,6 +186,20 @@ type fundingConfig struct {
|
||||
// DefaultRoutingPolicy is the default routing policy used when
|
||||
// initially announcing channels.
|
||||
DefaultRoutingPolicy htlcswitch.ForwardingPolicy
|
||||
|
||||
// NumRequiredConfs is a function closure that helps the funding
|
||||
// manager decide how many confirmations it should require for a
|
||||
// channel extended to it. The function is able to take into account
|
||||
// the amount of the channel, and any funds we'll be pushed in the
|
||||
// process to determine how many confirmations we'll require.
|
||||
NumRequiredConfs func(btcutil.Amount, btcutil.Amount) uint16
|
||||
|
||||
// RequiredRemoteDelay is a function that maps the total amount in a
|
||||
// proposed channel to the CSV delay that we'll require for the remote
|
||||
// party. Naturally a larger channel should require a higher CSV delay
|
||||
// in order to give us more time to claim funds in the case of a
|
||||
// contract breach.
|
||||
RequiredRemoteDelay func(btcutil.Amount) uint16
|
||||
}
|
||||
|
||||
// fundingManager acts as an orchestrator/bridge between the wallet's
|
||||
@ -206,11 +229,21 @@ type fundingManager struct {
|
||||
nonceMtx sync.RWMutex
|
||||
chanIDNonce uint64
|
||||
|
||||
// channelReservations is a map which houses the state of all pending
|
||||
// activeReservations is a map which houses the state of all pending
|
||||
// funding workflows.
|
||||
resMtx sync.RWMutex
|
||||
activeReservations map[serializedPubKey]pendingChannels
|
||||
|
||||
// signedReservations is a utility map that maps the permanent channel
|
||||
// ID of a funding reservation to its temporary channel ID. This is
|
||||
// required as mid funding flow, we switch to referencing the channel
|
||||
// by its full channel ID once the commitment transactions have been
|
||||
// signed by both parties.
|
||||
signedReservations map[lnwire.ChannelID][32]byte
|
||||
|
||||
// resMtx guards both of the maps above to ensure that all access is
|
||||
// goroutine stafe.
|
||||
resMtx sync.RWMutex
|
||||
|
||||
// fundingMsgs is a channel which receives wrapped wire messages
|
||||
// related to funding workflow from outside peers.
|
||||
fundingMsgs chan interface{}
|
||||
@ -244,6 +277,7 @@ func newFundingManager(cfg fundingConfig) (*fundingManager, error) {
|
||||
cfg: &cfg,
|
||||
chanIDKey: cfg.TempChanIDSeed,
|
||||
activeReservations: make(map[serializedPubKey]pendingChannels),
|
||||
signedReservations: make(map[lnwire.ChannelID][32]byte),
|
||||
newChanBarriers: make(map[lnwire.ChannelID]chan struct{}),
|
||||
fundingMsgs: make(chan interface{}, msgBufferSize),
|
||||
fundingRequests: make(chan *initFundingMsg, msgBufferSize),
|
||||
@ -268,7 +302,7 @@ func (f *fundingManager) Start() error {
|
||||
// down.
|
||||
// TODO(roasbeef): store height that funding finished?
|
||||
// * would then replace call below
|
||||
pendingChannels, err := f.cfg.Wallet.ChannelDB.FetchPendingChannels()
|
||||
pendingChannels, err := f.cfg.Wallet.Cfg.Database.FetchPendingChannels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -280,8 +314,8 @@ func (f *fundingManager) Start() error {
|
||||
for _, channel := range pendingChannels {
|
||||
f.barrierMtx.Lock()
|
||||
fndgLog.Tracef("Loading pending ChannelPoint(%v), creating chan "+
|
||||
"barrier", *channel.FundingOutpoint)
|
||||
chanID := lnwire.NewChanIDFromOutPoint(channel.FundingOutpoint)
|
||||
"barrier", channel.FundingOutpoint)
|
||||
chanID := lnwire.NewChanIDFromOutPoint(&channel.FundingOutpoint)
|
||||
f.newChanBarriers[chanID] = make(chan struct{})
|
||||
f.barrierMtx.Unlock()
|
||||
|
||||
@ -391,16 +425,17 @@ func (f *fundingManager) reservationCoordinator() {
|
||||
|
||||
for {
|
||||
select {
|
||||
|
||||
case msg := <-f.fundingMsgs:
|
||||
switch fmsg := msg.(type) {
|
||||
case *fundingRequestMsg:
|
||||
f.handleFundingRequest(fmsg)
|
||||
case *fundingResponseMsg:
|
||||
f.handleFundingResponse(fmsg)
|
||||
case *fundingCompleteMsg:
|
||||
f.handleFundingComplete(fmsg)
|
||||
case *fundingSignCompleteMsg:
|
||||
f.handleFundingSignComplete(fmsg)
|
||||
case *fundingOpenMsg:
|
||||
f.handleFundingOpen(fmsg)
|
||||
case *fundingAcceptMsg:
|
||||
f.handleFundingAccept(fmsg)
|
||||
case *fundingCreatedMsg:
|
||||
f.handleFundingCreated(fmsg)
|
||||
case *fundingSignedMsg:
|
||||
f.handleFundingSigned(fmsg)
|
||||
case *fundingLockedMsg:
|
||||
go f.handleFundingLocked(fmsg)
|
||||
case *fundingErrorMsg:
|
||||
@ -408,6 +443,7 @@ func (f *fundingManager) reservationCoordinator() {
|
||||
}
|
||||
case req := <-f.fundingRequests:
|
||||
f.handleInitFundingMsg(req)
|
||||
|
||||
case req := <-f.queries:
|
||||
switch msg := req.(type) {
|
||||
case *numPendingReq:
|
||||
@ -424,7 +460,7 @@ func (f *fundingManager) reservationCoordinator() {
|
||||
// handleNumPending handles a request for the total number of pending channels.
|
||||
func (f *fundingManager) handleNumPending(msg *numPendingReq) {
|
||||
// TODO(roasbeef): remove this method?
|
||||
dbPendingChannels, err := f.cfg.Wallet.ChannelDB.FetchPendingChannels()
|
||||
dbPendingChannels, err := f.cfg.Wallet.Cfg.Database.FetchPendingChannels()
|
||||
if err != nil {
|
||||
close(msg.resp)
|
||||
msg.err <- err
|
||||
@ -441,7 +477,7 @@ func (f *fundingManager) handleNumPending(msg *numPendingReq) {
|
||||
func (f *fundingManager) handlePendingChannels(msg *pendingChansReq) {
|
||||
var pendingChannels []*pendingChannel
|
||||
|
||||
dbPendingChannels, err := f.cfg.Wallet.ChannelDB.FetchPendingChannels()
|
||||
dbPendingChannels, err := f.cfg.Wallet.Cfg.Database.FetchPendingChannels()
|
||||
if err != nil {
|
||||
msg.resp <- nil
|
||||
msg.err <- err
|
||||
@ -451,10 +487,10 @@ func (f *fundingManager) handlePendingChannels(msg *pendingChansReq) {
|
||||
for _, dbPendingChan := range dbPendingChannels {
|
||||
pendingChan := &pendingChannel{
|
||||
identityPub: dbPendingChan.IdentityPub,
|
||||
channelPoint: dbPendingChan.ChanID,
|
||||
channelPoint: &dbPendingChan.FundingOutpoint,
|
||||
capacity: dbPendingChan.Capacity,
|
||||
localBalance: dbPendingChan.OurBalance,
|
||||
remoteBalance: dbPendingChan.TheirBalance,
|
||||
localBalance: dbPendingChan.LocalBalance,
|
||||
remoteBalance: dbPendingChan.RemoteBalance,
|
||||
}
|
||||
|
||||
pendingChannels = append(pendingChannels, pendingChan)
|
||||
@ -464,23 +500,28 @@ func (f *fundingManager) handlePendingChannels(msg *pendingChansReq) {
|
||||
msg.err <- nil
|
||||
}
|
||||
|
||||
// processFundingRequest sends a message to the fundingManager allowing it to
|
||||
// processFundingOpen sends a message to the fundingManager allowing it to
|
||||
// initiate the new funding workflow with the source peer.
|
||||
func (f *fundingManager) processFundingRequest(msg *lnwire.SingleFundingRequest,
|
||||
func (f *fundingManager) processFundingOpen(msg *lnwire.OpenChannel,
|
||||
peerAddress *lnwire.NetAddress) {
|
||||
f.fundingMsgs <- &fundingRequestMsg{msg, peerAddress}
|
||||
|
||||
f.fundingMsgs <- &fundingOpenMsg{msg, peerAddress}
|
||||
}
|
||||
|
||||
// handleFundingRequest creates an initial 'ChannelReservation' within
|
||||
// the wallet, then responds to the source peer with a single funder response
|
||||
// message progressing the funding workflow.
|
||||
// handleFundingOpen creates an initial 'ChannelReservation' within the wallet,
|
||||
// then responds to the source peer with an accept channel message progressing
|
||||
// the funding workflow.
|
||||
//
|
||||
// TODO(roasbeef): add error chan to all, let channelManager handle
|
||||
// error+propagate
|
||||
func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
||||
func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
|
||||
// Check number of pending channels to be smaller than maximum allowed
|
||||
// number and send ErrorGeneric to remote peer if condition is violated.
|
||||
// number and send ErrorGeneric to remote peer if condition is
|
||||
// violated.
|
||||
peerIDKey := newSerializedKey(fmsg.peerAddress.IdentityKey)
|
||||
|
||||
// TODO(roasbeef): modify to only accept a _single_ pending channel per
|
||||
// block unless white listed
|
||||
if len(f.activeReservations[peerIDKey]) >= cfg.MaxPendingChannels {
|
||||
errMsg := &lnwire.Error{
|
||||
ChanID: fmsg.msg.PendingChannelID,
|
||||
@ -504,7 +545,6 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
||||
fndgLog.Errorf("unable to query wallet: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !isSynced {
|
||||
errMsg := &lnwire.Error{
|
||||
ChanID: fmsg.msg.PendingChannelID,
|
||||
@ -518,38 +558,50 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(roasbeef): validate sanity of all params sent
|
||||
|
||||
msg := fmsg.msg
|
||||
amt := msg.FundingAmount
|
||||
delay := msg.CsvDelay
|
||||
|
||||
// TODO(roasbeef): error if funding flow already ongoing
|
||||
fndgLog.Infof("Recv'd fundingRequest(amt=%v, push=%v, delay=%v, pendingId=%x) "+
|
||||
"from peer(%x)", amt, msg.PushSatoshis, delay, msg.PendingChannelID,
|
||||
"from peer(%x)", amt, msg.PushAmount, msg.CsvDelay, msg.PendingChannelID,
|
||||
fmsg.peerAddress.IdentityKey.SerializeCompressed())
|
||||
|
||||
// TODO(roasbeef): dust limit should be made parameter into funding mgr
|
||||
// * will change on a per-chain basis
|
||||
ourDustLimit := lnwallet.DefaultDustLimit()
|
||||
theirDustlimit := msg.DustLimit
|
||||
|
||||
// Attempt to initialize a reservation within the wallet. If the wallet
|
||||
// has insufficient resources to create the channel, then the
|
||||
// reservation attempt may be rejected. Note that since we're on the
|
||||
// responding side of a single funder workflow, we don't commit any
|
||||
// funds to the channel ourselves.
|
||||
//
|
||||
// TODO(roasbeef): assuming this was an inbound connection, replace
|
||||
// port with default advertised port
|
||||
chainHash := chainhash.Hash(msg.ChainHash)
|
||||
reservation, err := f.cfg.Wallet.InitChannelReservation(amt, 0,
|
||||
msg.PushAmount, btcutil.Amount(msg.FeePerKiloWeight),
|
||||
fmsg.peerAddress.IdentityKey, fmsg.peerAddress.Address,
|
||||
uint16(fmsg.msg.ConfirmationDepth), delay, ourDustLimit,
|
||||
msg.PushSatoshis, msg.FeePerKw)
|
||||
&chainHash)
|
||||
if err != nil {
|
||||
// TODO(roasbeef): push ErrorGeneric message
|
||||
fndgLog.Errorf("Unable to initialize reservation: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
reservation.SetTheirDustLimit(theirDustlimit)
|
||||
// As we're the responder, we get to specify the number of
|
||||
// confirmations that we require before both of us consider the channel
|
||||
// open. We'll use out mapping to derive the proper number of
|
||||
// confirmations based on the amount of the channel, and also if any
|
||||
// funds are being pushed to us.
|
||||
numConfsReq := f.cfg.NumRequiredConfs(msg.FundingAmount, msg.PushAmount)
|
||||
reservation.SetNumConfsRequired(numConfsReq)
|
||||
|
||||
// We'll also validate and apply all the constraints the initiating
|
||||
// party is attempting to dictate for our commitment transaction.
|
||||
reservation.RequireLocalDelay(msg.CsvDelay)
|
||||
|
||||
fndgLog.Infof("Requiring %v confirmations for pendingChan(%x): "+
|
||||
"amt=%v, push_amt=%v", numConfsReq, fmsg.msg.PendingChannelID,
|
||||
amt, msg.PushAmount)
|
||||
|
||||
// Once the reservation has been created successfully, we add it to
|
||||
// this peers map of pending reservations to track this particular
|
||||
@ -560,6 +612,7 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
||||
}
|
||||
f.activeReservations[peerIDKey][msg.PendingChannelID] = &reservationWithCtx{
|
||||
reservation: reservation,
|
||||
chanAmt: amt,
|
||||
err: make(chan error, 1),
|
||||
peerAddress: fmsg.peerAddress,
|
||||
}
|
||||
@ -573,24 +626,32 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
||||
}
|
||||
}
|
||||
|
||||
// With our portion of the reservation initialized, process the
|
||||
// initiators contribution to the channel.
|
||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(msg.DeliveryPkScript,
|
||||
activeNetParams.Params)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("Unable to extract addresses from script: %v", err)
|
||||
cancelReservation()
|
||||
return
|
||||
}
|
||||
contribution := &lnwallet.ChannelContribution{
|
||||
// Using the RequiredRemoteDelay closure, we'll compute the remote CSV
|
||||
// delay we require given the total amount of funds within the channel.
|
||||
remoteCsvDelay := f.cfg.RequiredRemoteDelay(amt)
|
||||
|
||||
// With our parameters set, we'll now process their contribution so we
|
||||
// can move the funding workflow ahead.
|
||||
remoteContribution := &lnwallet.ChannelContribution{
|
||||
FundingAmount: amt,
|
||||
MultiSigKey: copyPubKey(msg.ChannelDerivationPoint),
|
||||
CommitKey: copyPubKey(msg.CommitmentKey),
|
||||
DeliveryAddress: addrs[0],
|
||||
FirstCommitmentPoint: msg.FirstCommitmentPoint,
|
||||
ChannelConfig: &channeldb.ChannelConfig{
|
||||
ChannelConstraints: channeldb.ChannelConstraints{
|
||||
DustLimit: msg.DustLimit,
|
||||
CsvDelay: delay,
|
||||
MaxPendingAmount: msg.MaxValueInFlight,
|
||||
ChanReserve: msg.ChannelReserve,
|
||||
MinHTLC: btcutil.Amount(msg.HtlcMinimum),
|
||||
MaxAcceptedHtlcs: msg.MaxAcceptedHTLCs,
|
||||
},
|
||||
CsvDelay: remoteCsvDelay,
|
||||
MultiSigKey: copyPubKey(msg.FundingKey),
|
||||
RevocationBasePoint: copyPubKey(msg.RevocationPoint),
|
||||
PaymentBasePoint: copyPubKey(msg.PaymentPoint),
|
||||
DelayBasePoint: copyPubKey(msg.DelayedPaymentPoint),
|
||||
},
|
||||
}
|
||||
if err := reservation.ProcessSingleContribution(contribution); err != nil {
|
||||
err = reservation.ProcessSingleContribution(remoteContribution)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("unable to add contribution reservation: %v", err)
|
||||
cancelReservation()
|
||||
return
|
||||
@ -602,35 +663,40 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
||||
// With the initiator's contribution recorded, respond with our
|
||||
// contribution in the next message of the workflow.
|
||||
ourContribution := reservation.OurContribution()
|
||||
deliveryScript, err := txscript.PayToAddrScript(ourContribution.DeliveryAddress)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("unable to convert address to pkscript: %v", err)
|
||||
cancelReservation()
|
||||
return
|
||||
fundingAccept := lnwire.AcceptChannel{
|
||||
PendingChannelID: msg.PendingChannelID,
|
||||
DustLimit: ourContribution.DustLimit,
|
||||
MaxValueInFlight: ourContribution.MaxPendingAmount,
|
||||
ChannelReserve: ourContribution.ChanReserve,
|
||||
MinAcceptDepth: uint32(numConfsReq),
|
||||
HtlcMinimum: uint32(ourContribution.MinHTLC),
|
||||
CsvDelay: uint16(remoteCsvDelay),
|
||||
FundingKey: ourContribution.MultiSigKey,
|
||||
RevocationPoint: ourContribution.RevocationBasePoint,
|
||||
PaymentPoint: ourContribution.PaymentBasePoint,
|
||||
DelayedPaymentPoint: ourContribution.DelayBasePoint,
|
||||
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
|
||||
}
|
||||
fundingResp := lnwire.NewSingleFundingResponse(msg.PendingChannelID,
|
||||
ourContribution.RevocationKey, ourContribution.CommitKey,
|
||||
ourContribution.MultiSigKey, ourContribution.CsvDelay,
|
||||
deliveryScript, ourDustLimit, msg.ConfirmationDepth)
|
||||
|
||||
if err := f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, fundingResp); err != nil {
|
||||
err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, &fundingAccept)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("unable to send funding response to peer: %v", err)
|
||||
cancelReservation()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// processFundingRequest sends a message to the fundingManager allowing it to
|
||||
// processFundingAccept sends a message to the fundingManager allowing it to
|
||||
// continue the second phase of a funding workflow with the target peer.
|
||||
func (f *fundingManager) processFundingResponse(msg *lnwire.SingleFundingResponse,
|
||||
func (f *fundingManager) processFundingResponse(msg *lnwire.AcceptChannel,
|
||||
peerAddress *lnwire.NetAddress) {
|
||||
f.fundingMsgs <- &fundingResponseMsg{msg, peerAddress}
|
||||
|
||||
f.fundingMsgs <- &fundingAcceptMsg{msg, peerAddress}
|
||||
}
|
||||
|
||||
// handleFundingResponse processes a response to the workflow initiation sent
|
||||
// by the remote peer. This message then queues a message with the funding
|
||||
// handleFundingAceept processes a response to the workflow initiation sent by
|
||||
// the remote peer. This message then queues a message with the funding
|
||||
// outpoint, and a commitment signature to the remote peer.
|
||||
func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) {
|
||||
func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
|
||||
msg := fmsg.msg
|
||||
pendingChanID := fmsg.msg.PendingChannelID
|
||||
peerKey := fmsg.peerAddress.IdentityKey
|
||||
@ -649,32 +715,40 @@ func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(roasbeeef): perform sanity checks on all the params and
|
||||
// constraints
|
||||
|
||||
fndgLog.Infof("Recv'd fundingResponse for pendingID(%x)", pendingChanID)
|
||||
|
||||
resCtx.reservation.SetTheirDustLimit(msg.DustLimit)
|
||||
// We'll also specify the responder's preference for the number of
|
||||
// required confirmations, and also the CSV delay that they specify for
|
||||
// us within the reservation itself.
|
||||
resCtx.reservation.SetNumConfsRequired(uint16(msg.MinAcceptDepth))
|
||||
resCtx.reservation.RequireLocalDelay(uint16(msg.CsvDelay))
|
||||
|
||||
// The remote node has responded with their portion of the channel
|
||||
// contribution. At this point, we can process their contribution which
|
||||
// allows us to construct and sign both the commitment transaction, and
|
||||
// the funding transaction.
|
||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(msg.DeliveryPkScript,
|
||||
activeNetParams.Params)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("Unable to extract addresses from script: %v", err)
|
||||
cancelReservation()
|
||||
resCtx.err <- err
|
||||
return
|
||||
}
|
||||
contribution := &lnwallet.ChannelContribution{
|
||||
FundingAmount: 0,
|
||||
MultiSigKey: copyPubKey(msg.ChannelDerivationPoint),
|
||||
CommitKey: copyPubKey(msg.CommitmentKey),
|
||||
DeliveryAddress: addrs[0],
|
||||
RevocationKey: copyPubKey(msg.RevocationKey),
|
||||
remoteContribution := &lnwallet.ChannelContribution{
|
||||
FirstCommitmentPoint: msg.FirstCommitmentPoint,
|
||||
ChannelConfig: &channeldb.ChannelConfig{
|
||||
ChannelConstraints: channeldb.ChannelConstraints{
|
||||
DustLimit: msg.DustLimit,
|
||||
CsvDelay: msg.CsvDelay,
|
||||
MaxPendingAmount: msg.MaxValueInFlight,
|
||||
ChanReserve: msg.ChannelReserve,
|
||||
MinHTLC: btcutil.Amount(msg.HtlcMinimum),
|
||||
MaxAcceptedHtlcs: msg.MaxAcceptedHTLCs,
|
||||
},
|
||||
MultiSigKey: copyPubKey(msg.FundingKey),
|
||||
RevocationBasePoint: copyPubKey(msg.RevocationPoint),
|
||||
PaymentBasePoint: copyPubKey(msg.PaymentPoint),
|
||||
DelayBasePoint: copyPubKey(msg.DelayedPaymentPoint),
|
||||
},
|
||||
}
|
||||
if err := resCtx.reservation.ProcessContribution(contribution); err != nil {
|
||||
remoteContribution.CsvDelay = f.cfg.RequiredRemoteDelay(resCtx.chanAmt)
|
||||
err = resCtx.reservation.ProcessContribution(remoteContribution)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("Unable to process contribution from %v: %v",
|
||||
fmsg.peerAddress.IdentityKey, err)
|
||||
cancelReservation()
|
||||
@ -682,6 +756,9 @@ func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) {
|
||||
return
|
||||
}
|
||||
|
||||
fndgLog.Infof("pendingChan(%x): remote party proposes num_confs=%v, "+
|
||||
"csv_delay=%v", pendingChanID, msg.MinAcceptDepth, msg.CsvDelay)
|
||||
|
||||
// Now that we have their contribution, we can extract, then send over
|
||||
// both the funding out point and our signature for their version of
|
||||
// the commitment transaction to the remote peer.
|
||||
@ -705,16 +782,23 @@ func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) {
|
||||
f.newChanBarriers[channelID] = make(chan struct{})
|
||||
f.barrierMtx.Unlock()
|
||||
|
||||
// The next message that advances the funding flow will reference the
|
||||
// channel via its permanent channel ID, so we'll set up this mapping
|
||||
// so we can retrieve the reservation context once we get the
|
||||
// FundingSigned message.
|
||||
f.resMtx.Lock()
|
||||
f.signedReservations[channelID] = pendingChanID
|
||||
f.resMtx.Unlock()
|
||||
|
||||
fndgLog.Infof("Generated ChannelPoint(%v) for pendingID(%x)", outPoint,
|
||||
pendingChanID)
|
||||
|
||||
revocationKey := resCtx.reservation.OurContribution().RevocationKey
|
||||
obsfucator := resCtx.reservation.StateNumObfuscator()
|
||||
|
||||
fundingComplete := lnwire.NewSingleFundingComplete(pendingChanID, *outPoint,
|
||||
commitSig, revocationKey, obsfucator)
|
||||
|
||||
err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, fundingComplete)
|
||||
fundingCreated := &lnwire.FundingCreated{
|
||||
PendingChannelID: pendingChanID,
|
||||
FundingPoint: *outPoint,
|
||||
CommitSig: commitSig,
|
||||
}
|
||||
err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, fundingCreated)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("Unable to send funding complete message: %v", err)
|
||||
cancelReservation()
|
||||
@ -723,18 +807,19 @@ func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) {
|
||||
}
|
||||
}
|
||||
|
||||
// processFundingComplete queues a funding complete message coupled with the
|
||||
// processFundingCreated queues a funding complete message coupled with the
|
||||
// source peer to the fundingManager.
|
||||
func (f *fundingManager) processFundingComplete(msg *lnwire.SingleFundingComplete,
|
||||
func (f *fundingManager) processFundingCreated(msg *lnwire.FundingCreated,
|
||||
peerAddress *lnwire.NetAddress) {
|
||||
f.fundingMsgs <- &fundingCompleteMsg{msg, peerAddress}
|
||||
|
||||
f.fundingMsgs <- &fundingCreatedMsg{msg, peerAddress}
|
||||
}
|
||||
|
||||
// handleFundingComplete progresses the funding workflow when the daemon is on
|
||||
// handleFundingCreated progresses the funding workflow when the daemon is on
|
||||
// the responding side of a single funder workflow. Once this message has been
|
||||
// processed, a signature is sent to the remote peer allowing it to broadcast
|
||||
// the funding transaction, progressing the workflow into the final stage.
|
||||
func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
||||
func (f *fundingManager) handleFundingCreated(fmsg *fundingCreatedMsg) {
|
||||
peerKey := fmsg.peerAddress.IdentityKey
|
||||
pendingChanID := fmsg.msg.PendingChannelID
|
||||
|
||||
@ -757,19 +842,16 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
||||
// the commitment transaction. So at this point, we can validate the
|
||||
// initiator's commitment transaction, then send our own if it's valid.
|
||||
// TODO(roasbeef): make case (p vs P) consistent throughout
|
||||
fundingOut := fmsg.msg.FundingOutPoint
|
||||
fundingOut := fmsg.msg.FundingPoint
|
||||
fndgLog.Infof("completing pendingID(%x) with ChannelPoint(%v)",
|
||||
pendingChanID, fundingOut)
|
||||
|
||||
revokeKey := copyPubKey(fmsg.msg.RevocationKey)
|
||||
obsfucator := fmsg.msg.StateHintObsfucator
|
||||
commitSig := fmsg.msg.CommitSignature.Serialize()
|
||||
|
||||
// With all the necessary data available, attempt to advance the
|
||||
// funding workflow to the next stage. If this succeeds then the
|
||||
// funding transaction will broadcast after our next message.
|
||||
commitSig := fmsg.msg.CommitSig.Serialize()
|
||||
completeChan, err := resCtx.reservation.CompleteReservationSingle(
|
||||
revokeKey, &fundingOut, commitSig, obsfucator)
|
||||
&fundingOut, commitSig)
|
||||
if err != nil {
|
||||
// TODO(roasbeef): better error logging: peerID, channelID, etc.
|
||||
fndgLog.Errorf("unable to complete single reservation: %v", err)
|
||||
@ -777,18 +859,6 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
||||
return
|
||||
}
|
||||
|
||||
// With their signature for our version of the commitment transaction
|
||||
// verified, we can now send over our signature to the remote peer.
|
||||
// TODO(roasbeef): just have raw bytes in wire msg? avoids decoding
|
||||
// then decoding shortly afterwards.
|
||||
_, sig := resCtx.reservation.OurSignatures()
|
||||
ourCommitSig, err := btcec.ParseSignature(sig, btcec.S256())
|
||||
if err != nil {
|
||||
fndgLog.Errorf("unable to parse signature: %v", err)
|
||||
cancelReservation()
|
||||
return
|
||||
}
|
||||
|
||||
// A new channel has almost finished the funding process. In order to
|
||||
// properly synchronize with the writeHandler goroutine, we add a new
|
||||
// channel to the barriers map which will be closed once the channel is
|
||||
@ -802,9 +872,25 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
||||
fndgLog.Infof("sending signComplete for pendingID(%x) over ChannelPoint(%v)",
|
||||
pendingChanID, fundingOut)
|
||||
|
||||
signComplete := lnwire.NewSingleFundingSignComplete(pendingChanID, ourCommitSig)
|
||||
if err := f.cfg.SendToPeer(peerKey, signComplete); err != nil {
|
||||
fndgLog.Errorf("unable to send signComplete message: %v", err)
|
||||
// With their signature for our version of the commitment transaction
|
||||
// verified, we can now send over our signature to the remote peer.
|
||||
//
|
||||
// TODO(roasbeef): just have raw bytes in wire msg? avoids decoding
|
||||
// then decoding shortly afterwards.
|
||||
_, sig := resCtx.reservation.OurSignatures()
|
||||
ourCommitSig, err := btcec.ParseSignature(sig, btcec.S256())
|
||||
if err != nil {
|
||||
fndgLog.Errorf("unable to parse signature: %v", err)
|
||||
cancelReservation()
|
||||
return
|
||||
}
|
||||
|
||||
fundingSigned := &lnwire.FundingSigned{
|
||||
ChanID: channelID,
|
||||
CommitSig: ourCommitSig,
|
||||
}
|
||||
if err := f.cfg.SendToPeer(peerKey, fundingSigned); err != nil {
|
||||
fndgLog.Errorf("unable to send FundingSigned message: %v", err)
|
||||
cancelReservation()
|
||||
return
|
||||
}
|
||||
@ -816,6 +902,9 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
||||
f.localDiscoverySignals[channelID] = make(chan struct{})
|
||||
f.localDiscoveryMtx.Unlock()
|
||||
|
||||
// With this last message, our job as the responder is now complete.
|
||||
// We'll wait for the funding transaction to reach the specified number
|
||||
// of confirmations, then start normal operations.
|
||||
go func() {
|
||||
doneChan := make(chan struct{})
|
||||
go f.waitForFundingConfirmation(completeChan, doneChan)
|
||||
@ -825,26 +914,39 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
||||
}()
|
||||
}
|
||||
|
||||
// processFundingSignComplete sends a single funding sign complete message
|
||||
// along with the source peer to the funding manager.
|
||||
func (f *fundingManager) processFundingSignComplete(msg *lnwire.SingleFundingSignComplete,
|
||||
// processFundingSigned sends a single funding sign complete message along with
|
||||
// the source peer to the funding manager.
|
||||
func (f *fundingManager) processFundingSigned(msg *lnwire.FundingSigned,
|
||||
peerAddress *lnwire.NetAddress) {
|
||||
f.fundingMsgs <- &fundingSignCompleteMsg{msg, peerAddress}
|
||||
|
||||
f.fundingMsgs <- &fundingSignedMsg{msg, peerAddress}
|
||||
}
|
||||
|
||||
// handleFundingSignComplete processes the final message received in a single
|
||||
// funder workflow. Once this message is processed, the funding transaction is
|
||||
// handleFundingSigned processes the final message received in a single funder
|
||||
// workflow. Once this message is processed, the funding transaction is
|
||||
// broadcast. Once the funding transaction reaches a sufficient number of
|
||||
// confirmations, a message is sent to the responding peer along with a compact
|
||||
// encoding of the location of the channel within the blockchain.
|
||||
func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg) {
|
||||
chanID := fmsg.msg.PendingChannelID
|
||||
peerKey := fmsg.peerAddress.IdentityKey
|
||||
func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
|
||||
// As the funding signed message will reference the reservation by it's
|
||||
// permanent channel ID, we'll need to perform an intermediate look up
|
||||
// before we can obtain the reservation.
|
||||
f.resMtx.Lock()
|
||||
pendingChanID, ok := f.signedReservations[fmsg.msg.ChanID]
|
||||
delete(f.signedReservations, fmsg.msg.ChanID)
|
||||
f.resMtx.Unlock()
|
||||
if !ok {
|
||||
fndgLog.Warnf("Unable to find signed reservation for chan_id=%x",
|
||||
fmsg.msg.ChanID)
|
||||
return
|
||||
}
|
||||
|
||||
resCtx, err := f.getReservationCtx(peerKey, chanID)
|
||||
peerKey := fmsg.peerAddress.IdentityKey
|
||||
resCtx, err := f.getReservationCtx(fmsg.peerAddress.IdentityKey,
|
||||
pendingChanID)
|
||||
if err != nil {
|
||||
fndgLog.Warnf("can't find reservation (peerID:%v, chanID:%v)",
|
||||
peerKey, chanID)
|
||||
fndgLog.Warnf("Unable to find reservation (peerID:%v, chanID:%v)",
|
||||
peerKey, pendingChanID)
|
||||
return
|
||||
}
|
||||
|
||||
@ -860,20 +962,21 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
|
||||
// The remote peer has responded with a signature for our commitment
|
||||
// transaction. We'll verify the signature for validity, then commit
|
||||
// the state to disk as we can now open the channel.
|
||||
commitSig := fmsg.msg.CommitSignature.Serialize()
|
||||
commitSig := fmsg.msg.CommitSig.Serialize()
|
||||
completeChan, err := resCtx.reservation.CompleteReservation(nil, commitSig)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("unable to complete reservation sign complete: %v", err)
|
||||
fndgLog.Errorf("Unable to complete reservation sign complete: %v", err)
|
||||
resCtx.err <- err
|
||||
|
||||
if _, err := f.cancelReservationCtx(peerKey, chanID); err != nil {
|
||||
fndgLog.Errorf("unable to cancel reservation: %v", err)
|
||||
_, err := f.cancelReservationCtx(peerKey, pendingChanID)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("Unable to cancel reservation: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fndgLog.Infof("Finalizing pendingID(%x) over ChannelPoint(%v), "+
|
||||
"waiting for channel open on-chain", chanID, fundingPoint)
|
||||
"waiting for channel open on-chain", pendingChanID, fundingPoint)
|
||||
|
||||
// Send an update to the upstream client that the negotiation process
|
||||
// is over.
|
||||
@ -912,7 +1015,7 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
|
||||
},
|
||||
}
|
||||
|
||||
f.deleteReservationCtx(peerKey, fmsg.msg.PendingChannelID)
|
||||
f.deleteReservationCtx(peerKey, pendingChanID)
|
||||
}()
|
||||
}
|
||||
|
||||
@ -951,7 +1054,7 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open
|
||||
return
|
||||
}
|
||||
|
||||
fundingPoint := *completeChan.FundingOutpoint
|
||||
fundingPoint := completeChan.FundingOutpoint
|
||||
chanID := lnwire.NewChanIDFromOutPoint(&fundingPoint)
|
||||
|
||||
fndgLog.Infof("ChannelPoint(%v) is now active: ChannelID(%x)",
|
||||
@ -969,14 +1072,16 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open
|
||||
// Now that the channel has been fully confirmed, we'll mark it as open
|
||||
// within the database.
|
||||
completeChan.IsPending = false
|
||||
err = f.cfg.Wallet.ChannelDB.MarkChannelAsOpen(&fundingPoint,
|
||||
shortChanID)
|
||||
err = f.cfg.Wallet.Cfg.Database.MarkChannelAsOpen(&fundingPoint, shortChanID)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("error setting channel pending flag to false: "+
|
||||
"%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(roasbeef): ideally persistent state update for chan above
|
||||
// should be abstracted
|
||||
|
||||
// With the channel marked open, we'll create the state-machine object
|
||||
// which wraps the database state.
|
||||
channel, err := lnwallet.NewLightningChannel(nil, nil,
|
||||
@ -1019,16 +1124,16 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open
|
||||
return
|
||||
}
|
||||
|
||||
// processFundingLocked sends a message to the fundingManager allowing it to finish
|
||||
// the funding workflow.
|
||||
// processFundingLocked sends a message to the fundingManager allowing it to
|
||||
// finish the funding workflow.
|
||||
func (f *fundingManager) processFundingLocked(msg *lnwire.FundingLocked,
|
||||
peerAddress *lnwire.NetAddress) {
|
||||
|
||||
f.fundingMsgs <- &fundingLockedMsg{msg, peerAddress}
|
||||
}
|
||||
|
||||
// handleFundingLocked finalizes the channel funding process and enables the channel
|
||||
// to enter normal operating mode.
|
||||
// handleFundingLocked finalizes the channel funding process and enables the
|
||||
// channel to enter normal operating mode.
|
||||
func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
|
||||
f.localDiscoveryMtx.Lock()
|
||||
localDiscoverySignal, ok := f.localDiscoverySignals[fmsg.msg.ChanID]
|
||||
@ -1042,8 +1147,8 @@ func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
|
||||
// required to handle forwarded HTLC's.
|
||||
<-localDiscoverySignal
|
||||
|
||||
// With the signal received, we can now safely delete the entry from
|
||||
// the map.
|
||||
// With the signal received, we can now safely delete the entry
|
||||
// from the map.
|
||||
f.localDiscoveryMtx.Lock()
|
||||
delete(f.localDiscoverySignals, fmsg.msg.ChanID)
|
||||
f.localDiscoveryMtx.Unlock()
|
||||
@ -1060,10 +1165,19 @@ func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
|
||||
return
|
||||
}
|
||||
|
||||
// The funding locked message contains the next commitment point we'll
|
||||
// need to create the next commitment state for the remote party. So
|
||||
// we'll insert that into the channel now before passing it along to
|
||||
// other sub-systems.
|
||||
err = channel.InitNextRevocation(fmsg.msg.NextPerCommitmentPoint)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("unable to insert next commitment point: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// With the channel retrieved, we'll send the breach arbiter the new
|
||||
// channel so it can watch for attempts to breach the channel's
|
||||
// contract by the remote
|
||||
// party.
|
||||
// contract by the remote party.
|
||||
f.cfg.ArbiterChan <- channel
|
||||
|
||||
// Launch a defer so we _ensure_ that the channel barrier is properly
|
||||
@ -1248,6 +1362,9 @@ func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKe
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(roasbeef): add flag that indicates if should be announced or
|
||||
// not
|
||||
|
||||
f.cfg.SendAnnouncement(ann.chanAnn)
|
||||
f.cfg.SendAnnouncement(ann.chanUpdateAnn)
|
||||
f.cfg.SendAnnouncement(ann.chanProof)
|
||||
@ -1258,6 +1375,7 @@ func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKe
|
||||
// TODO(roasbeef): re-visit blocking nature..
|
||||
func (f *fundingManager) initFundingWorkflow(peerAddress *lnwire.NetAddress,
|
||||
req *openChanReq) {
|
||||
|
||||
f.fundingRequests <- &initFundingMsg{
|
||||
peerAddress: peerAddress,
|
||||
openChanReq: req,
|
||||
@ -1274,19 +1392,19 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
|
||||
localAmt = msg.localFundingAmt
|
||||
remoteAmt = msg.remoteFundingAmt
|
||||
capacity = localAmt + remoteAmt
|
||||
numConfs = msg.numConfs
|
||||
ourDustLimit = lnwallet.DefaultDustLimit()
|
||||
)
|
||||
|
||||
fndgLog.Infof("Initiating fundingRequest(localAmt=%v, remoteAmt=%v, "+
|
||||
"capacity=%v, numConfs=%v, addr=%v, dustLimit=%v)", localAmt,
|
||||
msg.pushAmt, capacity, numConfs, msg.peerAddress.Address,
|
||||
"capacity=%v, chainhash=%v, addr=%v, dustLimit=%v)", localAmt,
|
||||
msg.pushAmt, capacity, msg.chainHash, msg.peerAddress.Address,
|
||||
ourDustLimit)
|
||||
|
||||
// First, we'll query the fee estimator for a fee that should get the
|
||||
// commitment transaction into the next block (conf target of 1). We
|
||||
// target the next block here to ensure that we'll be able to execute a
|
||||
// timely unilateral channel closure if needed.
|
||||
//
|
||||
// TODO(roasbeef): shouldn't be targeting next block
|
||||
feePerWeight := btcutil.Amount(f.cfg.FeeEstimator.EstimateFeePerWeight(1))
|
||||
|
||||
@ -1295,11 +1413,11 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
|
||||
feePerKw := feePerWeight * 1000
|
||||
|
||||
// Initialize a funding reservation with the local wallet. If the
|
||||
// wallet doesn't have enough funds to commit to this channel, then
|
||||
// the request will fail, and be aborted.
|
||||
// wallet doesn't have enough funds to commit to this channel, then the
|
||||
// request will fail, and be aborted.
|
||||
reservation, err := f.cfg.Wallet.InitChannelReservation(capacity,
|
||||
localAmt, peerKey, msg.peerAddress.Address, uint16(numConfs), 4,
|
||||
ourDustLimit, msg.pushAmt, feePerKw)
|
||||
localAmt, msg.pushAmt, feePerKw, peerKey,
|
||||
msg.peerAddress.Address, &msg.chainHash)
|
||||
if err != nil {
|
||||
msg.err <- err
|
||||
return
|
||||
@ -1322,6 +1440,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
|
||||
}
|
||||
|
||||
f.activeReservations[peerIDKey][chanID] = &reservationWithCtx{
|
||||
chanAmt: capacity,
|
||||
reservation: reservation,
|
||||
peerAddress: msg.peerAddress,
|
||||
updates: msg.updates,
|
||||
@ -1329,35 +1448,38 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
|
||||
}
|
||||
f.resMtx.Unlock()
|
||||
|
||||
// Using the RequiredRemoteDelay closure, we'll compute the remote CSV
|
||||
// delay we require given the total amount of funds within the channel.
|
||||
remoteCsvDelay := f.cfg.RequiredRemoteDelay(capacity)
|
||||
|
||||
// TODO(roasbeef): require remote delay?
|
||||
|
||||
// Once the reservation has been created, and indexed, queue a funding
|
||||
// request to the remote peer, kicking off the funding workflow.
|
||||
contribution := reservation.OurContribution()
|
||||
deliveryScript, err := txscript.PayToAddrScript(contribution.DeliveryAddress)
|
||||
if err != nil {
|
||||
fndgLog.Errorf("Unable to convert address to pkscript: %v", err)
|
||||
msg.err <- err
|
||||
return
|
||||
}
|
||||
ourContribution := reservation.OurContribution()
|
||||
|
||||
fndgLog.Infof("Starting funding workflow with %v for pendingID(%x)",
|
||||
msg.peerAddress.Address, chanID)
|
||||
|
||||
// TODO(roasbeef): add FundingRequestFromContribution func
|
||||
fundingReq := lnwire.NewSingleFundingRequest(
|
||||
chanID,
|
||||
msg.channelType,
|
||||
msg.coinType,
|
||||
feePerKw,
|
||||
capacity,
|
||||
contribution.CsvDelay,
|
||||
contribution.CommitKey,
|
||||
contribution.MultiSigKey,
|
||||
deliveryScript,
|
||||
ourDustLimit,
|
||||
msg.pushAmt,
|
||||
numConfs,
|
||||
)
|
||||
if err := f.cfg.SendToPeer(peerKey, fundingReq); err != nil {
|
||||
fundingOpen := lnwire.OpenChannel{
|
||||
ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash,
|
||||
PendingChannelID: chanID,
|
||||
FundingAmount: capacity,
|
||||
PushAmount: msg.pushAmt,
|
||||
DustLimit: ourContribution.DustLimit,
|
||||
MaxValueInFlight: ourContribution.MaxPendingAmount,
|
||||
ChannelReserve: ourContribution.ChanReserve,
|
||||
HtlcMinimum: uint32(ourContribution.MinHTLC),
|
||||
FeePerKiloWeight: uint32(feePerKw),
|
||||
CsvDelay: uint16(remoteCsvDelay),
|
||||
MaxAcceptedHTLCs: ourContribution.MaxAcceptedHtlcs,
|
||||
FundingKey: ourContribution.MultiSigKey,
|
||||
RevocationPoint: ourContribution.RevocationBasePoint,
|
||||
PaymentPoint: ourContribution.PaymentBasePoint,
|
||||
DelayedPaymentPoint: ourContribution.DelayBasePoint,
|
||||
FirstCommitmentPoint: ourContribution.FirstCommitmentPoint,
|
||||
}
|
||||
if err := f.cfg.SendToPeer(peerKey, &fundingOpen); err != nil {
|
||||
fndgLog.Errorf("Unable to send funding request message: %v", err)
|
||||
msg.err <- err
|
||||
return
|
||||
@ -1429,49 +1551,52 @@ func (f *fundingManager) handleErrorMsg(fmsg *fundingErrorMsg) {
|
||||
// cancelReservationCtx do all needed work in order to securely cancel the
|
||||
// reservation.
|
||||
func (f *fundingManager) cancelReservationCtx(peerKey *btcec.PublicKey,
|
||||
chanID [32]byte) (*reservationWithCtx, error) {
|
||||
pendingChanID [32]byte) (*reservationWithCtx, error) {
|
||||
|
||||
fndgLog.Infof("Cancelling funding reservation for node_key=%x, "+
|
||||
"chan_id=%x", peerKey.SerializeCompressed(), chanID)
|
||||
"chan_id=%x", peerKey.SerializeCompressed(), pendingChanID)
|
||||
|
||||
ctx, err := f.getReservationCtx(peerKey, chanID)
|
||||
ctx, err := f.getReservationCtx(peerKey, pendingChanID)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("can't find reservation: %v",
|
||||
return nil, errors.Errorf("unable to find reservation: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
if err := ctx.reservation.Cancel(); err != nil {
|
||||
return nil, errors.Errorf("can't cancel reservation: %v",
|
||||
return nil, errors.Errorf("unable to cancel reservation: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
f.deleteReservationCtx(peerKey, chanID)
|
||||
f.deleteReservationCtx(peerKey, pendingChanID)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// deleteReservationCtx is needed in order to securely delete the reservation.
|
||||
// deleteReservationCtx deletes the reservation uniquely identified by the
|
||||
// target public key of the peer, and the specified pending channel ID.
|
||||
func (f *fundingManager) deleteReservationCtx(peerKey *btcec.PublicKey,
|
||||
chanID [32]byte) {
|
||||
pendingChanID [32]byte) {
|
||||
|
||||
// TODO(roasbeef): possibly cancel funding barrier in peer's
|
||||
// channelManager?
|
||||
peerIDKey := newSerializedKey(peerKey)
|
||||
f.resMtx.Lock()
|
||||
delete(f.activeReservations[peerIDKey], chanID)
|
||||
delete(f.activeReservations[peerIDKey], pendingChanID)
|
||||
f.resMtx.Unlock()
|
||||
}
|
||||
|
||||
// getReservationCtx returns the reservation context by peer id and channel id.
|
||||
// getReservationCtx returns the reservation context for a particular pending
|
||||
// channel ID for a target peer.
|
||||
func (f *fundingManager) getReservationCtx(peerKey *btcec.PublicKey,
|
||||
chanID [32]byte) (*reservationWithCtx, error) {
|
||||
pendingChanID [32]byte) (*reservationWithCtx, error) {
|
||||
|
||||
peerIDKey := newSerializedKey(peerKey)
|
||||
f.resMtx.RLock()
|
||||
resCtx, ok := f.activeReservations[peerIDKey][chanID]
|
||||
resCtx, ok := f.activeReservations[peerIDKey][pendingChanID]
|
||||
f.resMtx.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unknown channel (id: %v)", chanID)
|
||||
return nil, errors.Errorf("unknown channel (id: %v)",
|
||||
pendingChanID)
|
||||
}
|
||||
|
||||
return resCtx, nil
|
||||
|
18
server.go
18
server.go
@ -20,6 +20,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnwire"
|
||||
"github.com/lightningnetwork/lnd/routing"
|
||||
"github.com/roasbeef/btcd/btcec"
|
||||
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||
"github.com/roasbeef/btcd/connmgr"
|
||||
"github.com/roasbeef/btcutil"
|
||||
|
||||
@ -952,22 +953,20 @@ type disconnectPeerMsg struct {
|
||||
}
|
||||
|
||||
// openChanReq is a message sent to the server in order to request the
|
||||
// initiation of a channel funding workflow to the peer with either the specified
|
||||
// relative peer ID, or a global lightning ID.
|
||||
// initiation of a channel funding workflow to the peer with either the
|
||||
// specified relative peer ID, or a global lightning ID.
|
||||
type openChanReq struct {
|
||||
targetPeerID int32
|
||||
targetPubkey *btcec.PublicKey
|
||||
|
||||
// TODO(roasbeef): make enums in lnwire
|
||||
channelType uint8
|
||||
coinType uint64
|
||||
chainHash chainhash.Hash
|
||||
|
||||
localFundingAmt btcutil.Amount
|
||||
remoteFundingAmt btcutil.Amount
|
||||
|
||||
pushAmt btcutil.Amount
|
||||
|
||||
numConfs uint32
|
||||
// TODO(roasbeef): add ability to specify channel constraints as well
|
||||
|
||||
updates chan *lnrpc.OpenStatusUpdate
|
||||
err chan error
|
||||
@ -1247,10 +1246,9 @@ func (s *server) DisconnectPeer(pubKey *btcec.PublicKey) error {
|
||||
}
|
||||
|
||||
// OpenChannel sends a request to the server to open a channel to the specified
|
||||
// peer identified by ID with the passed channel funding paramters.
|
||||
// peer identified by ID with the passed channel funding parameters.
|
||||
func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey,
|
||||
localAmt, pushAmt btcutil.Amount,
|
||||
numConfs uint32) (chan *lnrpc.OpenStatusUpdate, chan error) {
|
||||
localAmt, pushAmt btcutil.Amount) (chan *lnrpc.OpenStatusUpdate, chan error) {
|
||||
|
||||
errChan := make(chan error, 1)
|
||||
updateChan := make(chan *lnrpc.OpenStatusUpdate, 1)
|
||||
@ -1258,9 +1256,9 @@ func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey,
|
||||
req := &openChanReq{
|
||||
targetPeerID: peerID,
|
||||
targetPubkey: nodeKey,
|
||||
chainHash: *activeNetParams.GenesisHash,
|
||||
localFundingAmt: localAmt,
|
||||
pushAmt: pushAmt,
|
||||
numConfs: numConfs,
|
||||
updates: updateChan,
|
||||
err: errChan,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user