rpc: refactor request validation and response marshal into functions

This commit is contained in:
Wilmer Paulino 2020-05-20 16:12:39 -07:00
parent 9dcaf4fbc6
commit 7616a3dc7f
No known key found for this signature in database
GPG Key ID: 6DF57B9F9514972F
2 changed files with 107 additions and 85 deletions

@ -1,9 +1,14 @@
package lnrpc package lnrpc
import ( import (
"encoding/hex"
"errors" "errors"
fmt "fmt"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -54,3 +59,88 @@ func UnmarshallAmt(amtSat, amtMsat int64) (lnwire.MilliSatoshi, error) {
return lnwire.MilliSatoshi(amtMsat), nil return lnwire.MilliSatoshi(amtMsat), nil
} }
// ParseConfs validates the minimum and maximum confirmation arguments of a
// ListUnspent request.
func ParseConfs(min, max int32) (int32, int32, error) {
switch {
// Ensure that the user didn't attempt to specify a negative number of
// confirmations, as that isn't possible.
case min < 0:
return 0, 0, fmt.Errorf("min confirmations must be >= 0")
// 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 min > max:
return 0, 0, fmt.Errorf("max confirmations must be >= min " +
"confirmations")
default:
return min, max, nil
}
}
// MarshalUtxos translates a []*lnwallet.Utxo into a []*lnrpc.Utxo.
func MarshalUtxos(utxos []*lnwallet.Utxo, activeNetParams *chaincfg.Params) (
[]*Utxo, error) {
res := make([]*Utxo, 0, len(utxos))
for _, utxo := range utxos {
// Translate lnwallet address type to the proper gRPC proto
// address type.
var addrType AddressType
switch utxo.AddressType {
case lnwallet.WitnessPubKey:
addrType = AddressType_WITNESS_PUBKEY_HASH
case lnwallet.NestedWitnessPubKey:
addrType = AddressType_NESTED_PUBKEY_HASH
case lnwallet.UnknownAddressType:
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 := &OutPoint{
TxidBytes: utxo.OutPoint.Hash[:],
TxidStr: utxo.OutPoint.Hash.String(),
OutputIndex: utxo.OutPoint.Index,
}
utxoResp := Utxo{
AddressType: addrType,
AmountSat: int64(utxo.Value),
PkScript: hex.EncodeToString(utxo.PkScript),
Outpoint: outpoint,
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,
)
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")
}
utxoResp.Address = outAddresses[0].String()
res = append(res, &utxoResp)
}
return res, nil
}

@ -924,30 +924,20 @@ func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
return &txHash, nil return &txHash, nil
} }
// ListUnspent returns useful information about each unspent output owned by // ListUnspent returns useful information about each unspent output owned by the
// the wallet, as reported by the underlying `ListUnspentWitness`; the // wallet, as reported by the underlying `ListUnspentWitness`; the information
// information returned is: outpoint, amount in satoshis, address, address // returned is: outpoint, amount in satoshis, address, address type,
// type, scriptPubKey in hex and number of confirmations. The result is // scriptPubKey in hex and number of confirmations. The result is filtered to
// filtered to contain outputs whose number of confirmations is between a // contain outputs whose number of confirmations is between a minimum and
// minimum and maximum number of confirmations specified by the user, with 0 // maximum number of confirmations specified by the user, with 0 meaning
// meaning unconfirmed. // unconfirmed.
func (r *rpcServer) ListUnspent(ctx context.Context, func (r *rpcServer) ListUnspent(ctx context.Context,
in *lnrpc.ListUnspentRequest) (*lnrpc.ListUnspentResponse, error) { in *lnrpc.ListUnspentRequest) (*lnrpc.ListUnspentResponse, error) {
minConfs := in.MinConfs // Validate the confirmation arguments.
maxConfs := in.MaxConfs minConfs, maxConfs, err := lnrpc.ParseConfs(in.MinConfs, in.MaxConfs)
if err != nil {
switch { return nil, err
// 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")
// 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 // With our arguments validated, we'll query the internal wallet for
@ -957,71 +947,11 @@ func (r *rpcServer) ListUnspent(ctx context.Context,
return nil, err return nil, err
} }
resp := &lnrpc.ListUnspentResponse{ rpcUtxos, err := lnrpc.MarshalUtxos(utxos, activeNetParams.Params)
Utxos: make([]*lnrpc.Utxo, 0, len(utxos)),
}
for _, utxo := range utxos {
// 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())
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.OutPoint{
TxidBytes: utxo.OutPoint.Hash[:],
TxidStr: utxo.OutPoint.Hash.String(),
OutputIndex: utxo.OutPoint.Index,
}
utxoResp := lnrpc.Utxo{
AddressType: addrType,
AmountSat: int64(utxo.Value),
PkScript: hex.EncodeToString(utxo.PkScript),
Outpoint: outpoint,
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,
)
if err != nil { if err != nil {
return nil, err 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")
}
utxoResp.Address = outAddresses[0].String()
resp.Utxos = append(resp.Utxos, &utxoResp)
}
maxStr := "" maxStr := ""
if maxConfs != math.MaxInt32 { if maxConfs != math.MaxInt32 {
maxStr = " max=" + fmt.Sprintf("%d", maxConfs) maxStr = " max=" + fmt.Sprintf("%d", maxConfs)
@ -1030,7 +960,9 @@ func (r *rpcServer) ListUnspent(ctx context.Context,
rpcsLog.Debugf("[listunspent] min=%v%v, generated utxos: %v", minConfs, rpcsLog.Debugf("[listunspent] min=%v%v, generated utxos: %v", minConfs,
maxStr, utxos) maxStr, utxos)
return resp, nil return &lnrpc.ListUnspentResponse{
Utxos: rpcUtxos,
}, nil
} }
// EstimateFee handles a request for estimating the fee for sending a // EstimateFee handles a request for estimating the fee for sending a