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:
2 files changed
+347
-224
No files matched your search
+339
-214
@@ -18,7 +18,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
"github.com/roasbeef/btcd/txscript"
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||||
"github.com/roasbeef/btcd/wire"
|
"github.com/roasbeef/btcd/wire"
|
||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
@@ -27,6 +27,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
// TODO(roasbeef): tune
|
// TODO(roasbeef): tune
|
||||||
msgBufferSize = 50
|
msgBufferSize = 50
|
||||||
|
|
||||||
|
defaultCsvDelay = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
// reservationWithCtx encapsulates a pending channel reservation. This wrapper
|
// 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
|
// 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
|
// used to respond to the caller in the case a channel workflow is initiated
|
||||||
// via a local signal such as RPC.
|
// via a local signal such as RPC.
|
||||||
|
//
|
||||||
// TODO(roasbeef): actually use the context package
|
// TODO(roasbeef): actually use the context package
|
||||||
// * deadlines, etc.
|
// * deadlines, etc.
|
||||||
type reservationWithCtx struct {
|
type reservationWithCtx struct {
|
||||||
reservation *lnwallet.ChannelReservation
|
reservation *lnwallet.ChannelReservation
|
||||||
peerAddress *lnwire.NetAddress
|
peerAddress *lnwire.NetAddress
|
||||||
|
|
||||||
|
chanAmt btcutil.Amount
|
||||||
|
|
||||||
updates chan *lnrpc.OpenStatusUpdate
|
updates chan *lnrpc.OpenStatusUpdate
|
||||||
err chan error
|
err chan error
|
||||||
}
|
}
|
||||||
@@ -55,35 +60,35 @@ type initFundingMsg struct {
|
|||||||
*openChanReq
|
*openChanReq
|
||||||
}
|
}
|
||||||
|
|
||||||
// fundingRequestMsg couples an lnwire.SingleFundingRequest message with the
|
// fundingOpenMsg couples an lnwire.OpenChannel message with the peer who sent
|
||||||
// peer who sent the message. This allows the funding manager to queue a
|
// the message. This allows the funding manager to queue a response directly to
|
||||||
// response directly to the peer, progressing the funding workflow.
|
// the peer, progressing the funding workflow.
|
||||||
type fundingRequestMsg struct {
|
type fundingOpenMsg struct {
|
||||||
msg *lnwire.SingleFundingRequest
|
msg *lnwire.OpenChannel
|
||||||
peerAddress *lnwire.NetAddress
|
peerAddress *lnwire.NetAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
// fundingResponseMsg couples an lnwire.SingleFundingResponse message with the
|
// fundingAcceptMsg couples an lnwire.AcceptChannel message with the peer who
|
||||||
// peer who sent the message. This allows the funding manager to queue a
|
// sent the message. This allows the funding manager to queue a response
|
||||||
// response directly to the peer, progressing the funding workflow.
|
// directly to the peer, progressing the funding workflow.
|
||||||
type fundingResponseMsg struct {
|
type fundingAcceptMsg struct {
|
||||||
msg *lnwire.SingleFundingResponse
|
msg *lnwire.AcceptChannel
|
||||||
peerAddress *lnwire.NetAddress
|
peerAddress *lnwire.NetAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
// fundingCompleteMsg couples an lnwire.SingleFundingComplete message with the
|
// fundingCreatedMsg couples an lnwire.FundingCreated message with the peer who
|
||||||
// peer who sent the message. This allows the funding manager to queue a
|
// sent the message. This allows the funding manager to queue a response
|
||||||
// response directly to the peer, progressing the funding workflow.
|
// directly to the peer, progressing the funding workflow.
|
||||||
type fundingCompleteMsg struct {
|
type fundingCreatedMsg struct {
|
||||||
msg *lnwire.SingleFundingComplete
|
msg *lnwire.FundingCreated
|
||||||
peerAddress *lnwire.NetAddress
|
peerAddress *lnwire.NetAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
// fundingSignCompleteMsg couples an lnwire.SingleFundingSignComplete message
|
// fundingSignedMsg couples an lnwire.FundingSigned message with the peer who
|
||||||
// with the peer who sent the message. This allows the funding manager to
|
// sent the message. This allows the funding manager to queue a response
|
||||||
// queue a response directly to the peer, progressing the funding workflow.
|
// directly to the peer, progressing the funding workflow.
|
||||||
type fundingSignCompleteMsg struct {
|
type fundingSignedMsg struct {
|
||||||
msg *lnwire.SingleFundingSignComplete
|
msg *lnwire.FundingSigned
|
||||||
peerAddress *lnwire.NetAddress
|
peerAddress *lnwire.NetAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,7 +108,8 @@ type fundingErrorMsg struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pendingChannels is a map instantiated per-peer which tracks all active
|
// 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
|
type pendingChannels map[[32]byte]*reservationWithCtx
|
||||||
|
|
||||||
// serializedPubKey is used within the FundingManager's activeReservations list
|
// serializedPubKey is used within the FundingManager's activeReservations list
|
||||||
@@ -137,7 +143,7 @@ type fundingConfig struct {
|
|||||||
|
|
||||||
// ArbiterChan allows the FundingManager to notify the BreachArbiter
|
// ArbiterChan allows the FundingManager to notify the BreachArbiter
|
||||||
// that a new channel has been created that should be observed to
|
// 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.
|
// commitment transaction.
|
||||||
ArbiterChan chan<- *lnwallet.LightningChannel
|
ArbiterChan chan<- *lnwallet.LightningChannel
|
||||||
|
|
||||||
@@ -150,6 +156,9 @@ type fundingConfig struct {
|
|||||||
// actual digest signed is the double sha-256 of the message. In the
|
// actual digest signed is the double sha-256 of the message. In the
|
||||||
// case that the private key corresponding to the passed public key
|
// case that the private key corresponding to the passed public key
|
||||||
// cannot be located, then an error is returned.
|
// 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)
|
SignMessage func(pubKey *btcec.PublicKey, msg []byte) (*btcec.Signature, error)
|
||||||
|
|
||||||
// SendAnnouncement is used by the FundingManager to announce newly
|
// SendAnnouncement is used by the FundingManager to announce newly
|
||||||
@@ -177,6 +186,20 @@ type fundingConfig struct {
|
|||||||
// DefaultRoutingPolicy is the default routing policy used when
|
// DefaultRoutingPolicy is the default routing policy used when
|
||||||
// initially announcing channels.
|
// initially announcing channels.
|
||||||
DefaultRoutingPolicy htlcswitch.ForwardingPolicy
|
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
|
// fundingManager acts as an orchestrator/bridge between the wallet's
|
||||||
@@ -206,11 +229,21 @@ type fundingManager struct {
|
|||||||
nonceMtx sync.RWMutex
|
nonceMtx sync.RWMutex
|
||||||
chanIDNonce uint64
|
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.
|
// funding workflows.
|
||||||
resMtx sync.RWMutex
|
|
||||||
activeReservations map[serializedPubKey]pendingChannels
|
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
|
// fundingMsgs is a channel which receives wrapped wire messages
|
||||||
// related to funding workflow from outside peers.
|
// related to funding workflow from outside peers.
|
||||||
fundingMsgs chan interface{}
|
fundingMsgs chan interface{}
|
||||||
@@ -244,6 +277,7 @@ func newFundingManager(cfg fundingConfig) (*fundingManager, error) {
|
|||||||
cfg: &cfg,
|
cfg: &cfg,
|
||||||
chanIDKey: cfg.TempChanIDSeed,
|
chanIDKey: cfg.TempChanIDSeed,
|
||||||
activeReservations: make(map[serializedPubKey]pendingChannels),
|
activeReservations: make(map[serializedPubKey]pendingChannels),
|
||||||
|
signedReservations: make(map[lnwire.ChannelID][32]byte),
|
||||||
newChanBarriers: make(map[lnwire.ChannelID]chan struct{}),
|
newChanBarriers: make(map[lnwire.ChannelID]chan struct{}),
|
||||||
fundingMsgs: make(chan interface{}, msgBufferSize),
|
fundingMsgs: make(chan interface{}, msgBufferSize),
|
||||||
fundingRequests: make(chan *initFundingMsg, msgBufferSize),
|
fundingRequests: make(chan *initFundingMsg, msgBufferSize),
|
||||||
@@ -268,7 +302,7 @@ func (f *fundingManager) Start() error {
|
|||||||
// down.
|
// down.
|
||||||
// TODO(roasbeef): store height that funding finished?
|
// TODO(roasbeef): store height that funding finished?
|
||||||
// * would then replace call below
|
// * would then replace call below
|
||||||
pendingChannels, err := f.cfg.Wallet.ChannelDB.FetchPendingChannels()
|
pendingChannels, err := f.cfg.Wallet.Cfg.Database.FetchPendingChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -280,8 +314,8 @@ func (f *fundingManager) Start() error {
|
|||||||
for _, channel := range pendingChannels {
|
for _, channel := range pendingChannels {
|
||||||
f.barrierMtx.Lock()
|
f.barrierMtx.Lock()
|
||||||
fndgLog.Tracef("Loading pending ChannelPoint(%v), creating chan "+
|
fndgLog.Tracef("Loading pending ChannelPoint(%v), creating chan "+
|
||||||
"barrier", *channel.FundingOutpoint)
|
"barrier", channel.FundingOutpoint)
|
||||||
chanID := lnwire.NewChanIDFromOutPoint(channel.FundingOutpoint)
|
chanID := lnwire.NewChanIDFromOutPoint(&channel.FundingOutpoint)
|
||||||
f.newChanBarriers[chanID] = make(chan struct{})
|
f.newChanBarriers[chanID] = make(chan struct{})
|
||||||
f.barrierMtx.Unlock()
|
f.barrierMtx.Unlock()
|
||||||
|
|
||||||
@@ -391,16 +425,17 @@ func (f *fundingManager) reservationCoordinator() {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
||||||
case msg := <-f.fundingMsgs:
|
case msg := <-f.fundingMsgs:
|
||||||
switch fmsg := msg.(type) {
|
switch fmsg := msg.(type) {
|
||||||
case *fundingRequestMsg:
|
case *fundingOpenMsg:
|
||||||
f.handleFundingRequest(fmsg)
|
f.handleFundingOpen(fmsg)
|
||||||
case *fundingResponseMsg:
|
case *fundingAcceptMsg:
|
||||||
f.handleFundingResponse(fmsg)
|
f.handleFundingAccept(fmsg)
|
||||||
case *fundingCompleteMsg:
|
case *fundingCreatedMsg:
|
||||||
f.handleFundingComplete(fmsg)
|
f.handleFundingCreated(fmsg)
|
||||||
case *fundingSignCompleteMsg:
|
case *fundingSignedMsg:
|
||||||
f.handleFundingSignComplete(fmsg)
|
f.handleFundingSigned(fmsg)
|
||||||
case *fundingLockedMsg:
|
case *fundingLockedMsg:
|
||||||
go f.handleFundingLocked(fmsg)
|
go f.handleFundingLocked(fmsg)
|
||||||
case *fundingErrorMsg:
|
case *fundingErrorMsg:
|
||||||
@@ -408,6 +443,7 @@ func (f *fundingManager) reservationCoordinator() {
|
|||||||
}
|
}
|
||||||
case req := <-f.fundingRequests:
|
case req := <-f.fundingRequests:
|
||||||
f.handleInitFundingMsg(req)
|
f.handleInitFundingMsg(req)
|
||||||
|
|
||||||
case req := <-f.queries:
|
case req := <-f.queries:
|
||||||
switch msg := req.(type) {
|
switch msg := req.(type) {
|
||||||
case *numPendingReq:
|
case *numPendingReq:
|
||||||
@@ -424,7 +460,7 @@ func (f *fundingManager) reservationCoordinator() {
|
|||||||
// handleNumPending handles a request for the total number of pending channels.
|
// handleNumPending handles a request for the total number of pending channels.
|
||||||
func (f *fundingManager) handleNumPending(msg *numPendingReq) {
|
func (f *fundingManager) handleNumPending(msg *numPendingReq) {
|
||||||
// TODO(roasbeef): remove this method?
|
// TODO(roasbeef): remove this method?
|
||||||
dbPendingChannels, err := f.cfg.Wallet.ChannelDB.FetchPendingChannels()
|
dbPendingChannels, err := f.cfg.Wallet.Cfg.Database.FetchPendingChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
close(msg.resp)
|
close(msg.resp)
|
||||||
msg.err <- err
|
msg.err <- err
|
||||||
@@ -441,7 +477,7 @@ func (f *fundingManager) handleNumPending(msg *numPendingReq) {
|
|||||||
func (f *fundingManager) handlePendingChannels(msg *pendingChansReq) {
|
func (f *fundingManager) handlePendingChannels(msg *pendingChansReq) {
|
||||||
var pendingChannels []*pendingChannel
|
var pendingChannels []*pendingChannel
|
||||||
|
|
||||||
dbPendingChannels, err := f.cfg.Wallet.ChannelDB.FetchPendingChannels()
|
dbPendingChannels, err := f.cfg.Wallet.Cfg.Database.FetchPendingChannels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg.resp <- nil
|
msg.resp <- nil
|
||||||
msg.err <- err
|
msg.err <- err
|
||||||
@@ -451,10 +487,10 @@ func (f *fundingManager) handlePendingChannels(msg *pendingChansReq) {
|
|||||||
for _, dbPendingChan := range dbPendingChannels {
|
for _, dbPendingChan := range dbPendingChannels {
|
||||||
pendingChan := &pendingChannel{
|
pendingChan := &pendingChannel{
|
||||||
identityPub: dbPendingChan.IdentityPub,
|
identityPub: dbPendingChan.IdentityPub,
|
||||||
channelPoint: dbPendingChan.ChanID,
|
channelPoint: &dbPendingChan.FundingOutpoint,
|
||||||
capacity: dbPendingChan.Capacity,
|
capacity: dbPendingChan.Capacity,
|
||||||
localBalance: dbPendingChan.OurBalance,
|
localBalance: dbPendingChan.LocalBalance,
|
||||||
remoteBalance: dbPendingChan.TheirBalance,
|
remoteBalance: dbPendingChan.RemoteBalance,
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingChannels = append(pendingChannels, pendingChan)
|
pendingChannels = append(pendingChannels, pendingChan)
|
||||||
@@ -464,23 +500,28 @@ func (f *fundingManager) handlePendingChannels(msg *pendingChansReq) {
|
|||||||
msg.err <- nil
|
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.
|
// 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) {
|
peerAddress *lnwire.NetAddress) {
|
||||||
f.fundingMsgs <- &fundingRequestMsg{msg, peerAddress}
|
|
||||||
|
f.fundingMsgs <- &fundingOpenMsg{msg, peerAddress}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleFundingRequest creates an initial 'ChannelReservation' within
|
// handleFundingOpen creates an initial 'ChannelReservation' within the wallet,
|
||||||
// the wallet, then responds to the source peer with a single funder response
|
// then responds to the source peer with an accept channel message progressing
|
||||||
// message progressing the funding workflow.
|
// the funding workflow.
|
||||||
|
//
|
||||||
// TODO(roasbeef): add error chan to all, let channelManager handle
|
// TODO(roasbeef): add error chan to all, let channelManager handle
|
||||||
// error+propagate
|
// 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
|
// 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)
|
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 {
|
if len(f.activeReservations[peerIDKey]) >= cfg.MaxPendingChannels {
|
||||||
errMsg := &lnwire.Error{
|
errMsg := &lnwire.Error{
|
||||||
ChanID: fmsg.msg.PendingChannelID,
|
ChanID: fmsg.msg.PendingChannelID,
|
||||||
@@ -504,7 +545,6 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
|||||||
fndgLog.Errorf("unable to query wallet: %v", err)
|
fndgLog.Errorf("unable to query wallet: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isSynced {
|
if !isSynced {
|
||||||
errMsg := &lnwire.Error{
|
errMsg := &lnwire.Error{
|
||||||
ChanID: fmsg.msg.PendingChannelID,
|
ChanID: fmsg.msg.PendingChannelID,
|
||||||
@@ -518,38 +558,50 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(roasbeef): validate sanity of all params sent
|
||||||
|
|
||||||
msg := fmsg.msg
|
msg := fmsg.msg
|
||||||
amt := msg.FundingAmount
|
amt := msg.FundingAmount
|
||||||
delay := msg.CsvDelay
|
|
||||||
|
|
||||||
// TODO(roasbeef): error if funding flow already ongoing
|
// TODO(roasbeef): error if funding flow already ongoing
|
||||||
fndgLog.Infof("Recv'd fundingRequest(amt=%v, push=%v, delay=%v, pendingId=%x) "+
|
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())
|
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
|
// Attempt to initialize a reservation within the wallet. If the wallet
|
||||||
// has insufficient resources to create the channel, then the
|
// has insufficient resources to create the channel, then the
|
||||||
// reservation attempt may be rejected. Note that since we're on 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
|
// responding side of a single funder workflow, we don't commit any
|
||||||
// funds to the channel ourselves.
|
// funds to the channel ourselves.
|
||||||
|
//
|
||||||
// TODO(roasbeef): assuming this was an inbound connection, replace
|
// TODO(roasbeef): assuming this was an inbound connection, replace
|
||||||
// port with default advertised port
|
// port with default advertised port
|
||||||
|
chainHash := chainhash.Hash(msg.ChainHash)
|
||||||
reservation, err := f.cfg.Wallet.InitChannelReservation(amt, 0,
|
reservation, err := f.cfg.Wallet.InitChannelReservation(amt, 0,
|
||||||
|
msg.PushAmount, btcutil.Amount(msg.FeePerKiloWeight),
|
||||||
fmsg.peerAddress.IdentityKey, fmsg.peerAddress.Address,
|
fmsg.peerAddress.IdentityKey, fmsg.peerAddress.Address,
|
||||||
uint16(fmsg.msg.ConfirmationDepth), delay, ourDustLimit,
|
&chainHash)
|
||||||
msg.PushSatoshis, msg.FeePerKw)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(roasbeef): push ErrorGeneric message
|
// TODO(roasbeef): push ErrorGeneric message
|
||||||
fndgLog.Errorf("Unable to initialize reservation: %v", err)
|
fndgLog.Errorf("Unable to initialize reservation: %v", err)
|
||||||
return
|
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
|
// Once the reservation has been created successfully, we add it to
|
||||||
// this peers map of pending reservations to track this particular
|
// 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{
|
f.activeReservations[peerIDKey][msg.PendingChannelID] = &reservationWithCtx{
|
||||||
reservation: reservation,
|
reservation: reservation,
|
||||||
|
chanAmt: amt,
|
||||||
err: make(chan error, 1),
|
err: make(chan error, 1),
|
||||||
peerAddress: fmsg.peerAddress,
|
peerAddress: fmsg.peerAddress,
|
||||||
}
|
}
|
||||||
@@ -573,24 +626,32 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// With our portion of the reservation initialized, process the
|
// Using the RequiredRemoteDelay closure, we'll compute the remote CSV
|
||||||
// initiators contribution to the channel.
|
// delay we require given the total amount of funds within the channel.
|
||||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(msg.DeliveryPkScript,
|
remoteCsvDelay := f.cfg.RequiredRemoteDelay(amt)
|
||||||
activeNetParams.Params)
|
|
||||||
if err != nil {
|
// With our parameters set, we'll now process their contribution so we
|
||||||
fndgLog.Errorf("Unable to extract addresses from script: %v", err)
|
// can move the funding workflow ahead.
|
||||||
cancelReservation()
|
remoteContribution := &lnwallet.ChannelContribution{
|
||||||
return
|
|
||||||
}
|
|
||||||
contribution := &lnwallet.ChannelContribution{
|
|
||||||
FundingAmount: amt,
|
FundingAmount: amt,
|
||||||
MultiSigKey: copyPubKey(msg.ChannelDerivationPoint),
|
FirstCommitmentPoint: msg.FirstCommitmentPoint,
|
||||||
CommitKey: copyPubKey(msg.CommitmentKey),
|
ChannelConfig: &channeldb.ChannelConfig{
|
||||||
DeliveryAddress: addrs[0],
|
ChannelConstraints: channeldb.ChannelConstraints{
|
||||||
DustLimit: msg.DustLimit,
|
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)
|
fndgLog.Errorf("unable to add contribution reservation: %v", err)
|
||||||
cancelReservation()
|
cancelReservation()
|
||||||
return
|
return
|
||||||
@@ -602,35 +663,40 @@ func (f *fundingManager) handleFundingRequest(fmsg *fundingRequestMsg) {
|
|||||||
// With the initiator's contribution recorded, respond with our
|
// With the initiator's contribution recorded, respond with our
|
||||||
// contribution in the next message of the workflow.
|
// contribution in the next message of the workflow.
|
||||||
ourContribution := reservation.OurContribution()
|
ourContribution := reservation.OurContribution()
|
||||||
deliveryScript, err := txscript.PayToAddrScript(ourContribution.DeliveryAddress)
|
fundingAccept := lnwire.AcceptChannel{
|
||||||
if err != nil {
|
PendingChannelID: msg.PendingChannelID,
|
||||||
fndgLog.Errorf("unable to convert address to pkscript: %v", err)
|
DustLimit: ourContribution.DustLimit,
|
||||||
cancelReservation()
|
MaxValueInFlight: ourContribution.MaxPendingAmount,
|
||||||
return
|
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,
|
err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, &fundingAccept)
|
||||||
ourContribution.RevocationKey, ourContribution.CommitKey,
|
if err != nil {
|
||||||
ourContribution.MultiSigKey, ourContribution.CsvDelay,
|
|
||||||
deliveryScript, ourDustLimit, msg.ConfirmationDepth)
|
|
||||||
|
|
||||||
if err := f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, fundingResp); err != nil {
|
|
||||||
fndgLog.Errorf("unable to send funding response to peer: %v", err)
|
fndgLog.Errorf("unable to send funding response to peer: %v", err)
|
||||||
cancelReservation()
|
cancelReservation()
|
||||||
return
|
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.
|
// 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) {
|
peerAddress *lnwire.NetAddress) {
|
||||||
f.fundingMsgs <- &fundingResponseMsg{msg, peerAddress}
|
|
||||||
|
f.fundingMsgs <- &fundingAcceptMsg{msg, peerAddress}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleFundingResponse processes a response to the workflow initiation sent
|
// handleFundingAceept processes a response to the workflow initiation sent by
|
||||||
// by the remote peer. This message then queues a message with the funding
|
// the remote peer. This message then queues a message with the funding
|
||||||
// outpoint, and a commitment signature to the remote peer.
|
// outpoint, and a commitment signature to the remote peer.
|
||||||
func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) {
|
func (f *fundingManager) handleFundingAccept(fmsg *fundingAcceptMsg) {
|
||||||
msg := fmsg.msg
|
msg := fmsg.msg
|
||||||
pendingChanID := fmsg.msg.PendingChannelID
|
pendingChanID := fmsg.msg.PendingChannelID
|
||||||
peerKey := fmsg.peerAddress.IdentityKey
|
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)
|
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
|
// The remote node has responded with their portion of the channel
|
||||||
// contribution. At this point, we can process their contribution which
|
// contribution. At this point, we can process their contribution which
|
||||||
// allows us to construct and sign both the commitment transaction, and
|
// allows us to construct and sign both the commitment transaction, and
|
||||||
// the funding transaction.
|
// the funding transaction.
|
||||||
_, addrs, _, err := txscript.ExtractPkScriptAddrs(msg.DeliveryPkScript,
|
remoteContribution := &lnwallet.ChannelContribution{
|
||||||
activeNetParams.Params)
|
FirstCommitmentPoint: msg.FirstCommitmentPoint,
|
||||||
if err != nil {
|
ChannelConfig: &channeldb.ChannelConfig{
|
||||||
fndgLog.Errorf("Unable to extract addresses from script: %v", err)
|
ChannelConstraints: channeldb.ChannelConstraints{
|
||||||
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),
|
|
||||||
DustLimit: msg.DustLimit,
|
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",
|
fndgLog.Errorf("Unable to process contribution from %v: %v",
|
||||||
fmsg.peerAddress.IdentityKey, err)
|
fmsg.peerAddress.IdentityKey, err)
|
||||||
cancelReservation()
|
cancelReservation()
|
||||||
@@ -682,6 +756,9 @@ func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) {
|
|||||||
return
|
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
|
// Now that we have their contribution, we can extract, then send over
|
||||||
// both the funding out point and our signature for their version of
|
// both the funding out point and our signature for their version of
|
||||||
// the commitment transaction to the remote peer.
|
// the commitment transaction to the remote peer.
|
||||||
@@ -705,16 +782,23 @@ func (f *fundingManager) handleFundingResponse(fmsg *fundingResponseMsg) {
|
|||||||
f.newChanBarriers[channelID] = make(chan struct{})
|
f.newChanBarriers[channelID] = make(chan struct{})
|
||||||
f.barrierMtx.Unlock()
|
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,
|
fndgLog.Infof("Generated ChannelPoint(%v) for pendingID(%x)", outPoint,
|
||||||
pendingChanID)
|
pendingChanID)
|
||||||
|
|
||||||
revocationKey := resCtx.reservation.OurContribution().RevocationKey
|
fundingCreated := &lnwire.FundingCreated{
|
||||||
obsfucator := resCtx.reservation.StateNumObfuscator()
|
PendingChannelID: pendingChanID,
|
||||||
|
FundingPoint: *outPoint,
|
||||||
fundingComplete := lnwire.NewSingleFundingComplete(pendingChanID, *outPoint,
|
CommitSig: commitSig,
|
||||||
commitSig, revocationKey, obsfucator)
|
}
|
||||||
|
err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, fundingCreated)
|
||||||
err = f.cfg.SendToPeer(fmsg.peerAddress.IdentityKey, fundingComplete)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Errorf("Unable to send funding complete message: %v", err)
|
fndgLog.Errorf("Unable to send funding complete message: %v", err)
|
||||||
cancelReservation()
|
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.
|
// source peer to the fundingManager.
|
||||||
func (f *fundingManager) processFundingComplete(msg *lnwire.SingleFundingComplete,
|
func (f *fundingManager) processFundingCreated(msg *lnwire.FundingCreated,
|
||||||
peerAddress *lnwire.NetAddress) {
|
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
|
// 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
|
// processed, a signature is sent to the remote peer allowing it to broadcast
|
||||||
// the funding transaction, progressing the workflow into the final stage.
|
// 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
|
peerKey := fmsg.peerAddress.IdentityKey
|
||||||
pendingChanID := fmsg.msg.PendingChannelID
|
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
|
// the commitment transaction. So at this point, we can validate the
|
||||||
// initiator's commitment transaction, then send our own if it's valid.
|
// initiator's commitment transaction, then send our own if it's valid.
|
||||||
// TODO(roasbeef): make case (p vs P) consistent throughout
|
// 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)",
|
fndgLog.Infof("completing pendingID(%x) with ChannelPoint(%v)",
|
||||||
pendingChanID, fundingOut)
|
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
|
// With all the necessary data available, attempt to advance the
|
||||||
// funding workflow to the next stage. If this succeeds then the
|
// funding workflow to the next stage. If this succeeds then the
|
||||||
// funding transaction will broadcast after our next message.
|
// funding transaction will broadcast after our next message.
|
||||||
|
commitSig := fmsg.msg.CommitSig.Serialize()
|
||||||
completeChan, err := resCtx.reservation.CompleteReservationSingle(
|
completeChan, err := resCtx.reservation.CompleteReservationSingle(
|
||||||
revokeKey, &fundingOut, commitSig, obsfucator)
|
&fundingOut, commitSig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(roasbeef): better error logging: peerID, channelID, etc.
|
// TODO(roasbeef): better error logging: peerID, channelID, etc.
|
||||||
fndgLog.Errorf("unable to complete single reservation: %v", err)
|
fndgLog.Errorf("unable to complete single reservation: %v", err)
|
||||||
@@ -777,18 +859,6 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
|||||||
return
|
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
|
// A new channel has almost finished the funding process. In order to
|
||||||
// properly synchronize with the writeHandler goroutine, we add a new
|
// properly synchronize with the writeHandler goroutine, we add a new
|
||||||
// channel to the barriers map which will be closed once the channel is
|
// 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)",
|
fndgLog.Infof("sending signComplete for pendingID(%x) over ChannelPoint(%v)",
|
||||||
pendingChanID, fundingOut)
|
pendingChanID, fundingOut)
|
||||||
|
|
||||||
signComplete := lnwire.NewSingleFundingSignComplete(pendingChanID, ourCommitSig)
|
// With their signature for our version of the commitment transaction
|
||||||
if err := f.cfg.SendToPeer(peerKey, signComplete); err != nil {
|
// verified, we can now send over our signature to the remote peer.
|
||||||
fndgLog.Errorf("unable to send signComplete message: %v", err)
|
//
|
||||||
|
// 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()
|
cancelReservation()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -816,6 +902,9 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
|||||||
f.localDiscoverySignals[channelID] = make(chan struct{})
|
f.localDiscoverySignals[channelID] = make(chan struct{})
|
||||||
f.localDiscoveryMtx.Unlock()
|
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() {
|
go func() {
|
||||||
doneChan := make(chan struct{})
|
doneChan := make(chan struct{})
|
||||||
go f.waitForFundingConfirmation(completeChan, doneChan)
|
go f.waitForFundingConfirmation(completeChan, doneChan)
|
||||||
@@ -825,26 +914,39 @@ func (f *fundingManager) handleFundingComplete(fmsg *fundingCompleteMsg) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// processFundingSignComplete sends a single funding sign complete message
|
// processFundingSigned sends a single funding sign complete message along with
|
||||||
// along with the source peer to the funding manager.
|
// the source peer to the funding manager.
|
||||||
func (f *fundingManager) processFundingSignComplete(msg *lnwire.SingleFundingSignComplete,
|
func (f *fundingManager) processFundingSigned(msg *lnwire.FundingSigned,
|
||||||
peerAddress *lnwire.NetAddress) {
|
peerAddress *lnwire.NetAddress) {
|
||||||
f.fundingMsgs <- &fundingSignCompleteMsg{msg, peerAddress}
|
|
||||||
|
f.fundingMsgs <- &fundingSignedMsg{msg, peerAddress}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleFundingSignComplete processes the final message received in a single
|
// handleFundingSigned processes the final message received in a single funder
|
||||||
// funder workflow. Once this message is processed, the funding transaction is
|
// workflow. Once this message is processed, the funding transaction is
|
||||||
// broadcast. Once the funding transaction reaches a sufficient number of
|
// broadcast. Once the funding transaction reaches a sufficient number of
|
||||||
// confirmations, a message is sent to the responding peer along with a compact
|
// confirmations, a message is sent to the responding peer along with a compact
|
||||||
// encoding of the location of the channel within the blockchain.
|
// encoding of the location of the channel within the blockchain.
|
||||||
func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg) {
|
func (f *fundingManager) handleFundingSigned(fmsg *fundingSignedMsg) {
|
||||||
chanID := fmsg.msg.PendingChannelID
|
// As the funding signed message will reference the reservation by it's
|
||||||
peerKey := fmsg.peerAddress.IdentityKey
|
// 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 {
|
if err != nil {
|
||||||
fndgLog.Warnf("can't find reservation (peerID:%v, chanID:%v)",
|
fndgLog.Warnf("Unable to find reservation (peerID:%v, chanID:%v)",
|
||||||
peerKey, chanID)
|
peerKey, pendingChanID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,20 +962,21 @@ func (f *fundingManager) handleFundingSignComplete(fmsg *fundingSignCompleteMsg)
|
|||||||
// The remote peer has responded with a signature for our commitment
|
// The remote peer has responded with a signature for our commitment
|
||||||
// transaction. We'll verify the signature for validity, then commit
|
// transaction. We'll verify the signature for validity, then commit
|
||||||
// the state to disk as we can now open the channel.
|
// 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)
|
completeChan, err := resCtx.reservation.CompleteReservation(nil, commitSig)
|
||||||
if err != nil {
|
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
|
resCtx.err <- err
|
||||||
|
|
||||||
if _, err := f.cancelReservationCtx(peerKey, chanID); err != nil {
|
_, err := f.cancelReservationCtx(peerKey, pendingChanID)
|
||||||
fndgLog.Errorf("unable to cancel reservation: %v", err)
|
if err != nil {
|
||||||
|
fndgLog.Errorf("Unable to cancel reservation: %v", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fndgLog.Infof("Finalizing pendingID(%x) over ChannelPoint(%v), "+
|
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
|
// Send an update to the upstream client that the negotiation process
|
||||||
// is over.
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fundingPoint := *completeChan.FundingOutpoint
|
fundingPoint := completeChan.FundingOutpoint
|
||||||
chanID := lnwire.NewChanIDFromOutPoint(&fundingPoint)
|
chanID := lnwire.NewChanIDFromOutPoint(&fundingPoint)
|
||||||
|
|
||||||
fndgLog.Infof("ChannelPoint(%v) is now active: ChannelID(%x)",
|
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
|
// Now that the channel has been fully confirmed, we'll mark it as open
|
||||||
// within the database.
|
// within the database.
|
||||||
completeChan.IsPending = false
|
completeChan.IsPending = false
|
||||||
err = f.cfg.Wallet.ChannelDB.MarkChannelAsOpen(&fundingPoint,
|
err = f.cfg.Wallet.Cfg.Database.MarkChannelAsOpen(&fundingPoint, shortChanID)
|
||||||
shortChanID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fndgLog.Errorf("error setting channel pending flag to false: "+
|
fndgLog.Errorf("error setting channel pending flag to false: "+
|
||||||
"%v", err)
|
"%v", err)
|
||||||
return
|
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
|
// With the channel marked open, we'll create the state-machine object
|
||||||
// which wraps the database state.
|
// which wraps the database state.
|
||||||
channel, err := lnwallet.NewLightningChannel(nil, nil,
|
channel, err := lnwallet.NewLightningChannel(nil, nil,
|
||||||
@@ -1019,16 +1124,16 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// processFundingLocked sends a message to the fundingManager allowing it to finish
|
// processFundingLocked sends a message to the fundingManager allowing it to
|
||||||
// the funding workflow.
|
// finish the funding workflow.
|
||||||
func (f *fundingManager) processFundingLocked(msg *lnwire.FundingLocked,
|
func (f *fundingManager) processFundingLocked(msg *lnwire.FundingLocked,
|
||||||
peerAddress *lnwire.NetAddress) {
|
peerAddress *lnwire.NetAddress) {
|
||||||
|
|
||||||
f.fundingMsgs <- &fundingLockedMsg{msg, peerAddress}
|
f.fundingMsgs <- &fundingLockedMsg{msg, peerAddress}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleFundingLocked finalizes the channel funding process and enables the channel
|
// handleFundingLocked finalizes the channel funding process and enables the
|
||||||
// to enter normal operating mode.
|
// channel to enter normal operating mode.
|
||||||
func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
|
func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
|
||||||
f.localDiscoveryMtx.Lock()
|
f.localDiscoveryMtx.Lock()
|
||||||
localDiscoverySignal, ok := f.localDiscoverySignals[fmsg.msg.ChanID]
|
localDiscoverySignal, ok := f.localDiscoverySignals[fmsg.msg.ChanID]
|
||||||
@@ -1042,8 +1147,8 @@ func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
|
|||||||
// required to handle forwarded HTLC's.
|
// required to handle forwarded HTLC's.
|
||||||
<-localDiscoverySignal
|
<-localDiscoverySignal
|
||||||
|
|
||||||
// With the signal received, we can now safely delete the entry from
|
// With the signal received, we can now safely delete the entry
|
||||||
// the map.
|
// from the map.
|
||||||
f.localDiscoveryMtx.Lock()
|
f.localDiscoveryMtx.Lock()
|
||||||
delete(f.localDiscoverySignals, fmsg.msg.ChanID)
|
delete(f.localDiscoverySignals, fmsg.msg.ChanID)
|
||||||
f.localDiscoveryMtx.Unlock()
|
f.localDiscoveryMtx.Unlock()
|
||||||
@@ -1060,10 +1165,19 @@ func (f *fundingManager) handleFundingLocked(fmsg *fundingLockedMsg) {
|
|||||||
return
|
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
|
// With the channel retrieved, we'll send the breach arbiter the new
|
||||||
// channel so it can watch for attempts to breach the channel's
|
// channel so it can watch for attempts to breach the channel's
|
||||||
// contract by the remote
|
// contract by the remote party.
|
||||||
// party.
|
|
||||||
f.cfg.ArbiterChan <- channel
|
f.cfg.ArbiterChan <- channel
|
||||||
|
|
||||||
// Launch a defer so we _ensure_ that the channel barrier is properly
|
// Launch a defer so we _ensure_ that the channel barrier is properly
|
||||||
@@ -1248,6 +1362,9 @@ func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(roasbeef): add flag that indicates if should be announced or
|
||||||
|
// not
|
||||||
|
|
||||||
f.cfg.SendAnnouncement(ann.chanAnn)
|
f.cfg.SendAnnouncement(ann.chanAnn)
|
||||||
f.cfg.SendAnnouncement(ann.chanUpdateAnn)
|
f.cfg.SendAnnouncement(ann.chanUpdateAnn)
|
||||||
f.cfg.SendAnnouncement(ann.chanProof)
|
f.cfg.SendAnnouncement(ann.chanProof)
|
||||||
@@ -1258,6 +1375,7 @@ func (f *fundingManager) announceChannel(localIDKey, remoteIDKey, localFundingKe
|
|||||||
// TODO(roasbeef): re-visit blocking nature..
|
// TODO(roasbeef): re-visit blocking nature..
|
||||||
func (f *fundingManager) initFundingWorkflow(peerAddress *lnwire.NetAddress,
|
func (f *fundingManager) initFundingWorkflow(peerAddress *lnwire.NetAddress,
|
||||||
req *openChanReq) {
|
req *openChanReq) {
|
||||||
|
|
||||||
f.fundingRequests <- &initFundingMsg{
|
f.fundingRequests <- &initFundingMsg{
|
||||||
peerAddress: peerAddress,
|
peerAddress: peerAddress,
|
||||||
openChanReq: req,
|
openChanReq: req,
|
||||||
@@ -1274,19 +1392,19 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
|
|||||||
localAmt = msg.localFundingAmt
|
localAmt = msg.localFundingAmt
|
||||||
remoteAmt = msg.remoteFundingAmt
|
remoteAmt = msg.remoteFundingAmt
|
||||||
capacity = localAmt + remoteAmt
|
capacity = localAmt + remoteAmt
|
||||||
numConfs = msg.numConfs
|
|
||||||
ourDustLimit = lnwallet.DefaultDustLimit()
|
ourDustLimit = lnwallet.DefaultDustLimit()
|
||||||
)
|
)
|
||||||
|
|
||||||
fndgLog.Infof("Initiating fundingRequest(localAmt=%v, remoteAmt=%v, "+
|
fndgLog.Infof("Initiating fundingRequest(localAmt=%v, remoteAmt=%v, "+
|
||||||
"capacity=%v, numConfs=%v, addr=%v, dustLimit=%v)", localAmt,
|
"capacity=%v, chainhash=%v, addr=%v, dustLimit=%v)", localAmt,
|
||||||
msg.pushAmt, capacity, numConfs, msg.peerAddress.Address,
|
msg.pushAmt, capacity, msg.chainHash, msg.peerAddress.Address,
|
||||||
ourDustLimit)
|
ourDustLimit)
|
||||||
|
|
||||||
// First, we'll query the fee estimator for a fee that should get the
|
// 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
|
// 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
|
// target the next block here to ensure that we'll be able to execute a
|
||||||
// timely unilateral channel closure if needed.
|
// timely unilateral channel closure if needed.
|
||||||
|
//
|
||||||
// TODO(roasbeef): shouldn't be targeting next block
|
// TODO(roasbeef): shouldn't be targeting next block
|
||||||
feePerWeight := btcutil.Amount(f.cfg.FeeEstimator.EstimateFeePerWeight(1))
|
feePerWeight := btcutil.Amount(f.cfg.FeeEstimator.EstimateFeePerWeight(1))
|
||||||
|
|
||||||
@@ -1295,11 +1413,11 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
|
|||||||
feePerKw := feePerWeight * 1000
|
feePerKw := feePerWeight * 1000
|
||||||
|
|
||||||
// Initialize a funding reservation with the local wallet. If the
|
// Initialize a funding reservation with the local wallet. If the
|
||||||
// wallet doesn't have enough funds to commit to this channel, then
|
// wallet doesn't have enough funds to commit to this channel, then the
|
||||||
// the request will fail, and be aborted.
|
// request will fail, and be aborted.
|
||||||
reservation, err := f.cfg.Wallet.InitChannelReservation(capacity,
|
reservation, err := f.cfg.Wallet.InitChannelReservation(capacity,
|
||||||
localAmt, peerKey, msg.peerAddress.Address, uint16(numConfs), 4,
|
localAmt, msg.pushAmt, feePerKw, peerKey,
|
||||||
ourDustLimit, msg.pushAmt, feePerKw)
|
msg.peerAddress.Address, &msg.chainHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg.err <- err
|
msg.err <- err
|
||||||
return
|
return
|
||||||
@@ -1322,6 +1440,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
f.activeReservations[peerIDKey][chanID] = &reservationWithCtx{
|
f.activeReservations[peerIDKey][chanID] = &reservationWithCtx{
|
||||||
|
chanAmt: capacity,
|
||||||
reservation: reservation,
|
reservation: reservation,
|
||||||
peerAddress: msg.peerAddress,
|
peerAddress: msg.peerAddress,
|
||||||
updates: msg.updates,
|
updates: msg.updates,
|
||||||
@@ -1329,35 +1448,38 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
|
|||||||
}
|
}
|
||||||
f.resMtx.Unlock()
|
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
|
// Once the reservation has been created, and indexed, queue a funding
|
||||||
// request to the remote peer, kicking off the funding workflow.
|
// request to the remote peer, kicking off the funding workflow.
|
||||||
contribution := reservation.OurContribution()
|
ourContribution := 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
|
|
||||||
}
|
|
||||||
|
|
||||||
fndgLog.Infof("Starting funding workflow with %v for pendingID(%x)",
|
fndgLog.Infof("Starting funding workflow with %v for pendingID(%x)",
|
||||||
msg.peerAddress.Address, chanID)
|
msg.peerAddress.Address, chanID)
|
||||||
|
|
||||||
// TODO(roasbeef): add FundingRequestFromContribution func
|
fundingOpen := lnwire.OpenChannel{
|
||||||
fundingReq := lnwire.NewSingleFundingRequest(
|
ChainHash: *f.cfg.Wallet.Cfg.NetParams.GenesisHash,
|
||||||
chanID,
|
PendingChannelID: chanID,
|
||||||
msg.channelType,
|
FundingAmount: capacity,
|
||||||
msg.coinType,
|
PushAmount: msg.pushAmt,
|
||||||
feePerKw,
|
DustLimit: ourContribution.DustLimit,
|
||||||
capacity,
|
MaxValueInFlight: ourContribution.MaxPendingAmount,
|
||||||
contribution.CsvDelay,
|
ChannelReserve: ourContribution.ChanReserve,
|
||||||
contribution.CommitKey,
|
HtlcMinimum: uint32(ourContribution.MinHTLC),
|
||||||
contribution.MultiSigKey,
|
FeePerKiloWeight: uint32(feePerKw),
|
||||||
deliveryScript,
|
CsvDelay: uint16(remoteCsvDelay),
|
||||||
ourDustLimit,
|
MaxAcceptedHTLCs: ourContribution.MaxAcceptedHtlcs,
|
||||||
msg.pushAmt,
|
FundingKey: ourContribution.MultiSigKey,
|
||||||
numConfs,
|
RevocationPoint: ourContribution.RevocationBasePoint,
|
||||||
)
|
PaymentPoint: ourContribution.PaymentBasePoint,
|
||||||
if err := f.cfg.SendToPeer(peerKey, fundingReq); err != nil {
|
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)
|
fndgLog.Errorf("Unable to send funding request message: %v", err)
|
||||||
msg.err <- err
|
msg.err <- err
|
||||||
return
|
return
|
||||||
@@ -1429,49 +1551,52 @@ func (f *fundingManager) handleErrorMsg(fmsg *fundingErrorMsg) {
|
|||||||
// cancelReservationCtx do all needed work in order to securely cancel the
|
// cancelReservationCtx do all needed work in order to securely cancel the
|
||||||
// reservation.
|
// reservation.
|
||||||
func (f *fundingManager) cancelReservationCtx(peerKey *btcec.PublicKey,
|
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, "+
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Errorf("can't find reservation: %v",
|
return nil, errors.Errorf("unable to find reservation: %v",
|
||||||
err)
|
err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ctx.reservation.Cancel(); err != nil {
|
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)
|
err)
|
||||||
}
|
}
|
||||||
|
|
||||||
f.deleteReservationCtx(peerKey, chanID)
|
f.deleteReservationCtx(peerKey, pendingChanID)
|
||||||
return ctx, nil
|
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,
|
func (f *fundingManager) deleteReservationCtx(peerKey *btcec.PublicKey,
|
||||||
chanID [32]byte) {
|
pendingChanID [32]byte) {
|
||||||
|
|
||||||
// TODO(roasbeef): possibly cancel funding barrier in peer's
|
// TODO(roasbeef): possibly cancel funding barrier in peer's
|
||||||
// channelManager?
|
// channelManager?
|
||||||
peerIDKey := newSerializedKey(peerKey)
|
peerIDKey := newSerializedKey(peerKey)
|
||||||
f.resMtx.Lock()
|
f.resMtx.Lock()
|
||||||
delete(f.activeReservations[peerIDKey], chanID)
|
delete(f.activeReservations[peerIDKey], pendingChanID)
|
||||||
f.resMtx.Unlock()
|
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,
|
func (f *fundingManager) getReservationCtx(peerKey *btcec.PublicKey,
|
||||||
chanID [32]byte) (*reservationWithCtx, error) {
|
pendingChanID [32]byte) (*reservationWithCtx, error) {
|
||||||
|
|
||||||
peerIDKey := newSerializedKey(peerKey)
|
peerIDKey := newSerializedKey(peerKey)
|
||||||
f.resMtx.RLock()
|
f.resMtx.RLock()
|
||||||
resCtx, ok := f.activeReservations[peerIDKey][chanID]
|
resCtx, ok := f.activeReservations[peerIDKey][pendingChanID]
|
||||||
f.resMtx.RUnlock()
|
f.resMtx.RUnlock()
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.Errorf("unknown channel (id: %v)", chanID)
|
return nil, errors.Errorf("unknown channel (id: %v)",
|
||||||
|
pendingChanID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return resCtx, nil
|
return resCtx, nil
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwire"
|
"github.com/lightningnetwork/lnd/lnwire"
|
||||||
"github.com/lightningnetwork/lnd/routing"
|
"github.com/lightningnetwork/lnd/routing"
|
||||||
"github.com/roasbeef/btcd/btcec"
|
"github.com/roasbeef/btcd/btcec"
|
||||||
|
"github.com/roasbeef/btcd/chaincfg/chainhash"
|
||||||
"github.com/roasbeef/btcd/connmgr"
|
"github.com/roasbeef/btcd/connmgr"
|
||||||
"github.com/roasbeef/btcutil"
|
"github.com/roasbeef/btcutil"
|
||||||
|
|
||||||
@@ -952,22 +953,20 @@ type disconnectPeerMsg struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// openChanReq is a message sent to the server in order to request the
|
// 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
|
// initiation of a channel funding workflow to the peer with either the
|
||||||
// relative peer ID, or a global lightning ID.
|
// specified relative peer ID, or a global lightning ID.
|
||||||
type openChanReq struct {
|
type openChanReq struct {
|
||||||
targetPeerID int32
|
targetPeerID int32
|
||||||
targetPubkey *btcec.PublicKey
|
targetPubkey *btcec.PublicKey
|
||||||
|
|
||||||
// TODO(roasbeef): make enums in lnwire
|
chainHash chainhash.Hash
|
||||||
channelType uint8
|
|
||||||
coinType uint64
|
|
||||||
|
|
||||||
localFundingAmt btcutil.Amount
|
localFundingAmt btcutil.Amount
|
||||||
remoteFundingAmt btcutil.Amount
|
remoteFundingAmt btcutil.Amount
|
||||||
|
|
||||||
pushAmt btcutil.Amount
|
pushAmt btcutil.Amount
|
||||||
|
|
||||||
numConfs uint32
|
// TODO(roasbeef): add ability to specify channel constraints as well
|
||||||
|
|
||||||
updates chan *lnrpc.OpenStatusUpdate
|
updates chan *lnrpc.OpenStatusUpdate
|
||||||
err chan error
|
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
|
// 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,
|
func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey,
|
||||||
localAmt, pushAmt btcutil.Amount,
|
localAmt, pushAmt btcutil.Amount) (chan *lnrpc.OpenStatusUpdate, chan error) {
|
||||||
numConfs uint32) (chan *lnrpc.OpenStatusUpdate, chan error) {
|
|
||||||
|
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
updateChan := make(chan *lnrpc.OpenStatusUpdate, 1)
|
updateChan := make(chan *lnrpc.OpenStatusUpdate, 1)
|
||||||
@@ -1258,9 +1256,9 @@ func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey,
|
|||||||
req := &openChanReq{
|
req := &openChanReq{
|
||||||
targetPeerID: peerID,
|
targetPeerID: peerID,
|
||||||
targetPubkey: nodeKey,
|
targetPubkey: nodeKey,
|
||||||
|
chainHash: *activeNetParams.GenesisHash,
|
||||||
localFundingAmt: localAmt,
|
localFundingAmt: localAmt,
|
||||||
pushAmt: pushAmt,
|
pushAmt: pushAmt,
|
||||||
numConfs: numConfs,
|
|
||||||
updates: updateChan,
|
updates: updateChan,
|
||||||
err: errChan,
|
err: errChan,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user