diff --git a/rpcserver.go b/rpcserver.go index 6572dd380..4821828e2 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -684,48 +684,67 @@ func determineFeePerKw(feeEstimator lnwallet.FeeEstimator, targetConf int32, } } -// ListUnspent returns useful information about each unspent output -// owned by the wallet, as reported by the underlying `ListUnspentWitness`; -// the information returned is: outpoint, amount in satoshis, address, -// address type, scriptPubKey in hex and number of confirmations. -// The result is filtered to contain outputs whose number of confirmations -// is between a minimum and maximum number of confirmations specified by the -// user, with 0 meaning unconfirmed. +// ListUnspent returns useful information about each unspent output owned by +// the wallet, as reported by the underlying `ListUnspentWitness`; the +// information returned is: outpoint, amount in satoshis, address, address +// type, scriptPubKey in hex and number of confirmations. The result is +// filtered to contain outputs whose number of confirmations is between a +// minimum and maximum number of confirmations specified by the user, with 0 +// meaning unconfirmed. func (r *rpcServer) ListUnspent(ctx context.Context, in *lnrpc.ListUnspentRequest) (*lnrpc.ListUnspentResponse, error) { + minConfs := in.MinConfs maxConfs := in.MaxConfs - if minConfs < 0 { + switch { + // Ensure that the user didn't attempt to specify a negative number of + // confirmations, as that isn't possible. + case minConfs < 0: return nil, fmt.Errorf("min confirmations must be >= 0") - } - if minConfs > maxConfs { + + // We'll also ensure that the min number of confs is strictly less than + // or equal to the max number of confs for sanity. + case minConfs > maxConfs: return nil, fmt.Errorf("max confirmations must be >= min " + "confirmations") } + // With our arguments validated, we'll query the internal wallet for + // the set of UTXOs that match our query. utxos, err := r.server.cc.wallet.ListUnspentWitness(minConfs, maxConfs) if err != nil { return nil, err } - resp := &lnrpc.ListUnspentResponse{Utxos: []*lnrpc.Utxo{}} + resp := &lnrpc.ListUnspentResponse{ + Utxos: make([]*lnrpc.Utxo, 0, len(utxos)), + } + for _, utxo := range utxos { - // Translate lnwallet address type to gRPC proto address type: + // Translate lnwallet address type to the proper gRPC proto + // address type. var addrType lnrpc.AddressType switch utxo.AddressType { + case lnwallet.WitnessPubKey: addrType = lnrpc.AddressType_WITNESS_PUBKEY_HASH + case lnwallet.NestedWitnessPubKey: addrType = lnrpc.AddressType_NESTED_PUBKEY_HASH + case lnwallet.UnknownAddressType: - rpcsLog.Warnf("[listunspent] utxo with address of unknown "+ - "type ignored: %v", utxo.OutPoint.String()) + rpcsLog.Warnf("[listunspent] utxo with address of "+ + "unknown type ignored: %v", + utxo.OutPoint.String()) continue + default: return nil, fmt.Errorf("invalid utxo address type") } + // Now that we know we have a proper mapping to an address, + // we'll convert the regular outpoint to an lnrpc variant. outpoint := &lnrpc.ChannelPoint{ FundingTxid: &lnrpc.ChannelPoint_FundingTxidStr{ FundingTxidStr: utxo.OutPoint.Hash.String(), @@ -741,17 +760,26 @@ func (r *rpcServer) ListUnspent(ctx context.Context, Confirmations: utxo.Confirmations, } + // Finally, we'll attempt to extract the raw address from the + // script so we can display a human friendly address to the end + // user. _, outAddresses, _, err := txscript.ExtractPkScriptAddrs( - utxo.PkScript, activeNetParams.Params) + utxo.PkScript, activeNetParams.Params, + ) if err != nil { return nil, err } + // If we can't properly locate a single address, then this was + // an error in our mapping, and we'll return an error back to + // the user. if len(outAddresses) != 1 { - return nil, fmt.Errorf("an output was unexpectedly multisig") + return nil, fmt.Errorf("an output was unexpectedly " + + "multisig") } utxoResp.Address = outAddresses[0].String() + resp.Utxos = append(resp.Utxos, &utxoResp) } @@ -759,8 +787,9 @@ func (r *rpcServer) ListUnspent(ctx context.Context, if maxConfs != math.MaxInt32 { maxStr = " max=" + fmt.Sprintf("%d", maxConfs) } - rpcsLog.Debugf("[listunspent] min=%v%v, generated utxos: %v", - minConfs, maxStr, utxos) + + rpcsLog.Debugf("[listunspent] min=%v%v, generated utxos: %v", minConfs, + maxStr, utxos) return resp, nil }