rpc: non-existence of a nursery report is no longer an error

This commit fixes a slight logic error that could render the
`pendingchannels` RPC unusable if a node was on the reciting end of a
channel force close with no time-locked balance. In such a case the
channel wouldn’t be sent to the utxoNursery, resulting in an “contract
not found error”.

To fix this behavior, we’ve created a typed error that can be checked
within the RPC, thus we no longer treat this routine case as an error
case.
This commit is contained in:
Olaoluwa Osuntokun 2017-05-14 19:20:26 -07:00
parent 459583ca04
commit 9c685433f3
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2
2 changed files with 29 additions and 17 deletions

@ -721,13 +721,14 @@ func (r *rpcServer) forceCloseChan(channel *lnwallet.LightningChannel) (*chainha
chanPoint := channel.ChannelPoint()
chanInfo := channel.StateSnapshot()
closeInfo := &channeldb.ChannelCloseSummary{
ChanPoint: *chanPoint,
ClosingTXID: closeTx.TxHash(),
RemotePub: &chanInfo.RemoteIdentity,
Capacity: chanInfo.Capacity,
OurBalance: chanInfo.LocalBalance,
CloseType: channeldb.ForceClose,
IsPending: true,
ChanPoint: *chanPoint,
ClosingTXID: closeTx.TxHash(),
RemotePub: &chanInfo.RemoteIdentity,
Capacity: chanInfo.Capacity,
SettledBalance: chanInfo.LocalBalance,
TimeLockedBalance: chanInfo.LocalBalance,
CloseType: channeldb.ForceClose,
IsPending: true,
}
if err := channel.DeleteState(closeInfo); err != nil {
return nil, err
@ -767,7 +768,8 @@ func (r *rpcServer) GetInfo(ctx context.Context,
isSynced, err := r.server.lnwallet.IsSynced()
if err != nil {
return nil, fmt.Errorf("unable to sync PoV of the wallet with current best block in the main chain: %v", err)
return nil, fmt.Errorf("unable to sync PoV of the wallet "+
"with current best block in the main chain: %v", err)
}
activeChains := make([]string, registeredChains.NumActiveChains())
@ -933,7 +935,7 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
RemoteNodePub: hex.EncodeToString(pub),
ChannelPoint: chanPoint.String(),
Capacity: int64(pendingClose.Capacity),
LocalBalance: int64(pendingClose.OurBalance),
LocalBalance: int64(pendingClose.SettledBalance),
}
closeTXID := pendingClose.ClosingTXID.String()
@ -961,9 +963,14 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
ClosingTxid: closeTXID,
}
// Query for the maturity state for this force closed
// channel. If we didn't have any time-locked outputs,
// then the nursery may not know of the contract.
nurseryInfo, err := r.server.utxoNursery.NurseryReport(&chanPoint)
if err != nil {
return nil, err
if err != nil && err != ErrContractNotFound {
return nil, fmt.Errorf("unable to obtain "+
"nursery report for ChannelPoint(%v): %v",
chanPoint, err)
}
// If the nursery knows of this channel, then we can
@ -996,7 +1003,6 @@ func (r *rpcServer) PendingChannels(ctx context.Context,
// ListChannels returns a description of all direct active, open channels the
// node knows of.
// TODO(roasbeef): add 'online' bit to response
func (r *rpcServer) ListChannels(ctx context.Context,
in *lnrpc.ListChannelsRequest) (*lnrpc.ListChannelsResponse, error) {

@ -70,6 +70,12 @@ var (
byteOrder = binary.BigEndian
)
var (
// ErrContractNotFound is returned when the nursery is unable to
// retreive information about a queried contract.
ErrContractNotFound = fmt.Errorf("unable to locate contract")
)
// witnessType determines how an output's witness will be generated. The
// default commitmentTimeLock type will generate a witness that will allow
// spending of a time-locked transaction enforced by CheckSequenceVerify.
@ -317,9 +323,9 @@ type incubationRequest struct {
func (u *utxoNursery) IncubateOutputs(closeSummary *lnwallet.ForceCloseSummary) {
var incReq incubationRequest
// It could be that our to-self output was below the dust limit. In that
// case the SignDescriptor would be nil and we would not have that output
// to incubate.
// It could be that our to-self output was below the dust limit. In
// that case the SignDescriptor would be nil and we would not have that
// output to incubate.
if closeSummary.SelfOutputSignDesc != nil {
outputAmt := btcutil.Amount(closeSummary.SelfOutputSignDesc.Output.Value)
selfOutput := &kidOutput{
@ -510,7 +516,7 @@ func (u *utxoNursery) NurseryReport(chanPoint *wire.OutPoint) (*contractMaturity
// entry for this particular contract.
indexInfo := indexBucket.Get(chanPointBytes)
if indexInfo == nil {
return fmt.Errorf("contract not found in index")
return ErrContractNotFound
}
// If an entry is found, then using the height store in
@ -519,7 +525,7 @@ func (u *utxoNursery) NurseryReport(chanPoint *wire.OutPoint) (*contractMaturity
height := indexInfo[:4]
heightRow := kgtnBucket.Get(height)
if heightRow == nil {
return fmt.Errorf("contract not found")
return ErrContractNotFound
}
// Once we have the entry itself, we'll slice of the