mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
226 lines
6.7 KiB
Go
226 lines
6.7 KiB
Go
package lnrpc
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/btcsuite/btcd/txscript"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/btcsuite/btcwallet/wallet"
|
|
"github.com/lightningnetwork/lnd/aliasmgr"
|
|
"github.com/lightningnetwork/lnd/fn"
|
|
"github.com/lightningnetwork/lnd/lnwallet"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
"golang.org/x/exp/maps"
|
|
)
|
|
|
|
var (
|
|
// ErrSatMsatMutualExclusive is returned when both a sat and an msat
|
|
// amount are set.
|
|
ErrSatMsatMutualExclusive = errors.New(
|
|
"sat and msat arguments are mutually exclusive",
|
|
)
|
|
)
|
|
|
|
// CalculateFeeLimit returns the fee limit in millisatoshis. If a percentage
|
|
// based fee limit has been requested, we'll factor in the ratio provided with
|
|
// the amount of the payment.
|
|
func CalculateFeeLimit(feeLimit *FeeLimit,
|
|
amount lnwire.MilliSatoshi) lnwire.MilliSatoshi {
|
|
|
|
switch feeLimit.GetLimit().(type) {
|
|
case *FeeLimit_Fixed:
|
|
return lnwire.NewMSatFromSatoshis(
|
|
btcutil.Amount(feeLimit.GetFixed()),
|
|
)
|
|
|
|
case *FeeLimit_FixedMsat:
|
|
return lnwire.MilliSatoshi(feeLimit.GetFixedMsat())
|
|
|
|
case *FeeLimit_Percent:
|
|
return amount * lnwire.MilliSatoshi(feeLimit.GetPercent()) / 100
|
|
|
|
default:
|
|
// Fall back to a sane default value that is based on the amount
|
|
// itself.
|
|
return lnwallet.DefaultRoutingFeeLimitForAmount(amount)
|
|
}
|
|
}
|
|
|
|
// UnmarshallAmt returns a strong msat type for a sat/msat pair of rpc fields.
|
|
func UnmarshallAmt(amtSat, amtMsat int64) (lnwire.MilliSatoshi, error) {
|
|
if amtSat != 0 && amtMsat != 0 {
|
|
return 0, ErrSatMsatMutualExclusive
|
|
}
|
|
|
|
if amtSat != 0 {
|
|
return lnwire.NewMSatFromSatoshis(btcutil.Amount(amtSat)), 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.TaprootPubkey:
|
|
addrType = AddressType_TAPROOT_PUBKEY
|
|
|
|
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 := MarshalOutPoint(&utxo.OutPoint)
|
|
|
|
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
|
|
}
|
|
|
|
// MarshallOutputType translates a txscript.ScriptClass into a
|
|
// lnrpc.OutputScriptType.
|
|
func MarshallOutputType(o txscript.ScriptClass) OutputScriptType {
|
|
// Translate txscript ScriptClass type to the proper gRPC proto
|
|
// output script type.
|
|
switch o {
|
|
case txscript.ScriptHashTy:
|
|
return OutputScriptType_SCRIPT_TYPE_SCRIPT_HASH
|
|
case txscript.WitnessV0PubKeyHashTy:
|
|
return OutputScriptType_SCRIPT_TYPE_WITNESS_V0_PUBKEY_HASH
|
|
case txscript.WitnessV0ScriptHashTy:
|
|
return OutputScriptType_SCRIPT_TYPE_WITNESS_V0_SCRIPT_HASH
|
|
case txscript.PubKeyTy:
|
|
return OutputScriptType_SCRIPT_TYPE_PUBKEY
|
|
case txscript.MultiSigTy:
|
|
return OutputScriptType_SCRIPT_TYPE_MULTISIG
|
|
case txscript.NullDataTy:
|
|
return OutputScriptType_SCRIPT_TYPE_NULLDATA
|
|
case txscript.NonStandardTy:
|
|
return OutputScriptType_SCRIPT_TYPE_NON_STANDARD
|
|
case txscript.WitnessUnknownTy:
|
|
return OutputScriptType_SCRIPT_TYPE_WITNESS_UNKNOWN
|
|
case txscript.WitnessV1TaprootTy:
|
|
return OutputScriptType_SCRIPT_TYPE_WITNESS_V1_TAPROOT
|
|
default:
|
|
return OutputScriptType_SCRIPT_TYPE_PUBKEY_HASH
|
|
}
|
|
}
|
|
|
|
// MarshalOutPoint converts a wire.OutPoint to its proto counterpart.
|
|
func MarshalOutPoint(op *wire.OutPoint) *OutPoint {
|
|
return &OutPoint{
|
|
TxidBytes: op.Hash[:],
|
|
TxidStr: op.Hash.String(),
|
|
OutputIndex: op.Index,
|
|
}
|
|
}
|
|
|
|
// UnmarshallCoinSelectionStrategy converts a lnrpc.CoinSelectionStrategy proto
|
|
// type to its wallet.CoinSelectionStrategy counterpart type, considering
|
|
// a global default strategy if necessary.
|
|
//
|
|
// The globalStrategy parameter specifies the default coin selection strategy
|
|
// to use if the strategy is set to STRATEGY_USE_GLOBAL_CONFIG. This allows
|
|
// flexibility in defining a default strategy at a global level.
|
|
func UnmarshallCoinSelectionStrategy(strategy CoinSelectionStrategy,
|
|
globalStrategy wallet.CoinSelectionStrategy) (
|
|
wallet.CoinSelectionStrategy, error) {
|
|
|
|
switch strategy {
|
|
case CoinSelectionStrategy_STRATEGY_USE_GLOBAL_CONFIG:
|
|
return globalStrategy, nil
|
|
|
|
case CoinSelectionStrategy_STRATEGY_LARGEST:
|
|
return wallet.CoinSelectionLargest, nil
|
|
|
|
case CoinSelectionStrategy_STRATEGY_RANDOM:
|
|
return wallet.CoinSelectionRandom, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown coin selection strategy "+
|
|
"%v", strategy)
|
|
}
|
|
}
|
|
|
|
// MarshalAliasMap converts a ScidAliasMap to its proto counterpart. This is
|
|
// used in various RPCs that handle scid alias mappings.
|
|
func MarshalAliasMap(scidMap aliasmgr.ScidAliasMap) []*AliasMap {
|
|
return fn.Map(func(base lnwire.ShortChannelID) *AliasMap {
|
|
return &AliasMap{
|
|
BaseScid: base.ToUint64(),
|
|
Aliases: fn.Map(func(a lnwire.ShortChannelID) uint64 {
|
|
return a.ToUint64()
|
|
}, scidMap[base]),
|
|
}
|
|
}, maps.Keys(scidMap))
|
|
}
|