pool/order/rpc_parse.go
2023-02-23 11:26:42 +01:00

568 lines
16 KiB
Go

package order
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"net"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr/musig2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/lightninglabs/pool/account"
"github.com/lightninglabs/pool/auctioneerrpc"
"github.com/lightninglabs/pool/poolrpc"
"github.com/lightninglabs/pool/terms"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/tor"
)
// ParseOption defines a set of functional param options that can be used
// to modify our we parse orders based on some optional directives.
type ParseOption func(*parseOptions)
// parseOptions houses the set of functional options used to parse RPC orders.
type parseOptions struct {
chanTypeSelector func() ChannelType
}
// ChanTypeSelector defines a function capable of selecting a channel type
// based on the version of an lnd node.
type ChanTypeSelector func() ChannelType
// WithDefaultChannelType allows a caller to select a default channel type
// based on the version of the lnd node attempting to create the order.
func WithDefaultChannelType(selector ChanTypeSelector) ParseOption {
return func(o *parseOptions) {
o.chanTypeSelector = selector
}
}
// defaultParseOptions returns the set of default parse options.
func defaultParseOptions() *parseOptions {
return &parseOptions{}
}
// ParseRPCOrder parses the incoming raw RPC order into the go native data
// types used in the order struct.
func ParseRPCOrder(version, leaseDuration uint32,
details *poolrpc.Order, parseOpts ...ParseOption) (*Kit, error) {
opts := defaultParseOptions()
for _, newOpt := range parseOpts {
newOpt(opts)
}
var nonce Nonce
copy(nonce[:], details.OrderNonce)
kit := NewKit(nonce)
// If the user didn't provide a nonce, we generate one.
if nonce == ZeroNonce {
preimageBytes, err := randomPreimage()
if err != nil {
return nil, fmt.Errorf("cannot generate nonce: %v", err)
}
var preimage lntypes.Preimage
copy(preimage[:], preimageBytes)
kit = NewKitWithPreimage(preimage)
}
copy(kit.AcctKey[:], details.TraderKey)
kit.AuctionType = AuctionType(details.AuctionType)
kit.Version = Version(version)
kit.FixedRate = details.RateFixed
kit.Amt = btcutil.Amount(details.Amt)
kit.MaxBatchFeeRate = chainfee.SatPerKWeight(
details.MaxBatchFeeRateSatPerKw,
)
kit.Units = NewSupplyFromSats(kit.Amt)
kit.UnitsUnfulfilled = kit.Units
kit.LeaseDuration = leaseDuration
switch {
// If a min units match constraint was not provided, return an error.
case details.MinUnitsMatch == 0:
return nil, errors.New("min units match must be greater than 0")
// The min units match must not exceed the total order units.
case kit.AuctionType != BTCOutboundLiquidity &&
details.MinUnitsMatch > uint32(kit.Units):
return nil, errors.New("min units match must not exceed " +
"total order units")
}
kit.MinUnitsMatch = SupplyUnit(details.MinUnitsMatch)
switch details.ChannelType {
// Default value, trader didn't specify a channel type.
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_UNKNOWN:
// If we have a chan type selector, we'll use that, otherwise
// we'll just use peer dependent channels as default.
if opts.chanTypeSelector != nil {
kit.ChannelType = opts.chanTypeSelector()
} else {
kit.ChannelType = ChannelTypePeerDependent
}
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_PEER_DEPENDENT:
kit.ChannelType = ChannelTypePeerDependent
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED:
kit.ChannelType = ChannelTypeScriptEnforced
default:
return nil, fmt.Errorf("unhandled channel type %v",
details.ChannelType)
}
if len(details.AllowedNodeIds) > 0 &&
len(details.NotAllowedNodeIds) > 0 {
return nil, errors.New("allowed and not allowed node ids set " +
"at the same time")
}
allowedNodeIDs, err := UnmarshalNodeIDSlice(details.AllowedNodeIds)
if err != nil {
return nil, fmt.Errorf("invalid allowed_node_ids: %v", err)
}
kit.AllowedNodeIDs = allowedNodeIDs
notAllowedNodeIDs, err := UnmarshalNodeIDSlice(
details.NotAllowedNodeIds,
)
if err != nil {
return nil, fmt.Errorf("invalid not_allowed_node_ids: %v", err)
}
kit.NotAllowedNodeIDs = notAllowedNodeIDs
kit.IsPublic = details.IsPublic
return kit, nil
}
// parseNodeAddrs parses the set of address in strong format as returned over
// the RPC layer into a proper interface we can use.
func parseNodeAddrs(rpcAddrs []*auctioneerrpc.NodeAddress,
orderIsAsk bool) ([]net.Addr, error) {
if len(rpcAddrs) == 0 && orderIsAsk {
return nil, fmt.Errorf("ask order doesn't include any node " +
"addrs")
}
nodeAddrs := make([]net.Addr, 0, len(rpcAddrs))
for _, rpcAddr := range rpcAddrs {
var (
addr net.Addr
err error
)
// Obtain the host to determine if this is a Tor address.
host, _, err := net.SplitHostPort(rpcAddr.Addr)
if err != nil {
host = rpcAddr.Addr
}
switch {
case tor.IsOnionHost(host):
addr, err = parseOnionAddr(rpcAddr.Addr)
default:
addr, err = net.ResolveTCPAddr(
rpcAddr.Network, rpcAddr.Addr,
)
}
if err != nil {
return nil, fmt.Errorf("unable to parse node "+
"addr: %v", err)
}
nodeAddrs = append(nodeAddrs, addr)
}
return nodeAddrs, nil
}
// ParseRPCServerOrder parses the incoming raw RPC server order into the go
// native data types used in the order struct.
func ParseRPCServerOrder(version uint32, details *auctioneerrpc.ServerOrder,
orderIsAsk bool, leaseDuration uint32) (*Kit, [33]byte, []net.Addr,
[33]byte, error) {
var (
nonce Nonce
nodeKey [33]byte
multiSigKey [33]byte
)
copy(nonce[:], details.OrderNonce)
kit := NewKit(nonce)
kit.AuctionType = AuctionType(details.AuctionType)
kit.Version = Version(version)
kit.FixedRate = details.RateFixed
kit.Amt = btcutil.Amount(details.Amt)
kit.Units = NewSupplyFromSats(kit.Amt)
kit.UnitsUnfulfilled = kit.Units
kit.MaxBatchFeeRate = chainfee.SatPerKWeight(
details.MaxBatchFeeRateSatPerKw,
)
kit.LeaseDuration = leaseDuration
// If the user didn't provide a nonce, we generate one.
if nonce == ZeroNonce {
preimageBytes, err := randomPreimage()
if err != nil {
return nil, nodeKey, nil, multiSigKey,
fmt.Errorf("cannot generate nonce: %v", err)
}
var preimage lntypes.Preimage
copy(preimage[:], preimageBytes)
kit = NewKitWithPreimage(preimage)
}
copy(kit.AcctKey[:], details.TraderKey)
nodePubKey, err := btcec.ParsePubKey(details.NodePub)
if err != nil {
return nil, nodeKey, nil, multiSigKey,
fmt.Errorf("unable to parse node pub key: %v",
err)
}
copy(nodeKey[:], nodePubKey.SerializeCompressed())
nodeAddrs, err := parseNodeAddrs(details.NodeAddr, orderIsAsk)
if err != nil {
return nil, nodeKey, nil, multiSigKey, err
}
multiSigPubkey, err := btcec.ParsePubKey(details.MultiSigKey)
if err != nil {
return nil, nodeKey, nodeAddrs, multiSigKey,
fmt.Errorf("unable to parse multi sig pub key: %v", err)
}
copy(multiSigKey[:], multiSigPubkey.SerializeCompressed())
switch details.ChannelType {
// Default value, trader didn't specify a channel type.
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_UNKNOWN:
kit.ChannelType = ChannelTypePeerDependent
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_PEER_DEPENDENT:
kit.ChannelType = ChannelTypePeerDependent
case auctioneerrpc.OrderChannelType_ORDER_CHANNEL_TYPE_SCRIPT_ENFORCED:
kit.ChannelType = ChannelTypeScriptEnforced
default:
return nil, nodeKey, nil, multiSigKey,
fmt.Errorf("unhandled channel type %v", details.ChannelType)
}
return kit, nodeKey, nodeAddrs, multiSigKey, nil
}
// ParseRPCServerAsk parses the incoming raw RPC server ask into the go
// native data types used in the order struct.
func ParseRPCServerAsk(details *auctioneerrpc.ServerAsk) (*MatchedOrder, error) {
var (
o = &MatchedOrder{}
kit *Kit
err error
)
kit, o.NodeKey, o.NodeAddrs, o.MultiSigKey, err = ParseRPCServerOrder(
details.Version, details.Details, true,
details.LeaseDurationBlocks,
)
if err != nil {
return nil, err
}
kit.LeaseDuration = details.LeaseDurationBlocks
announcement := ChannelAnnouncementConstraints(
details.AnnouncementConstraints,
)
confirmations := ChannelConfirmationConstraints(
details.ConfirmationConstraints,
)
o.Order = &Ask{
Kit: *kit,
AnnouncementConstraints: announcement,
ConfirmationConstraints: confirmations,
}
return o, nil
}
// ParseRPCServerBid parses the incoming raw RPC server bid into the go
// native data types used in the order struct.
func ParseRPCServerBid(details *auctioneerrpc.ServerBid) (*MatchedOrder, error) {
var (
o = &MatchedOrder{}
kit *Kit
err error
)
kit, o.NodeKey, o.NodeAddrs, o.MultiSigKey, err = ParseRPCServerOrder(
details.Version, details.Details, false,
details.LeaseDurationBlocks,
)
if err != nil {
return nil, err
}
o.Order = &Bid{
Kit: *kit,
SelfChanBalance: btcutil.Amount(details.SelfChanBalance),
UnannouncedChannel: details.UnannouncedChannel,
ZeroConfChannel: details.ZeroConfChannel,
}
return o, nil
}
// ParseRPCBatch parses the incoming raw RPC batch into the go native data types
// used by the order manager.
func ParseRPCBatch(prepareMsg *auctioneerrpc.OrderMatchPrepare) (*Batch,
error) {
b := &Batch{
Version: BatchVersion(prepareMsg.BatchVersion),
MatchedOrders: make(map[Nonce][]*MatchedOrder),
BatchTX: &wire.MsgTx{},
ClearingPrices: make(map[uint32]FixedRatePremium),
HeightHint: prepareMsg.BatchHeightHint,
}
// Parse matched orders market by market.
for leaseDuration, matchedMarket := range prepareMsg.MatchedMarkets {
orders := matchedMarket.MatchedOrders
for ourOrderHex, rpcMatchedOrders := range orders {
var ourOrder Nonce
ourOrderBytes, err := hex.DecodeString(ourOrderHex)
if err != nil {
return nil, fmt.Errorf("error parsing nonce: "+
"%v", err)
}
copy(ourOrder[:], ourOrderBytes)
matchedOrders, err := ParseRPCMatchedOrders(
rpcMatchedOrders,
)
if err != nil {
return nil, fmt.Errorf("error parsing matched "+
"order: %v", err)
}
// It is imperative that all matched orders have the
// correct lease duration. Otherwise assumptions in the
// batch verifier won't be correct. That's why we
// validate this here already.
for _, matchedOrder := range matchedOrders {
details := matchedOrder.Order.Details()
if details.LeaseDuration != leaseDuration {
return nil, fmt.Errorf("matched order "+
"%s has incorrect lease "+
"duration %d for bucket %d",
details.Nonce(),
details.LeaseDuration,
leaseDuration)
}
}
b.MatchedOrders[ourOrder] = matchedOrders
}
b.ClearingPrices[leaseDuration] = FixedRatePremium(
matchedMarket.ClearingPriceRate,
)
}
// Parse account diff.
for _, diff := range prepareMsg.ChargedAccounts {
var acctKeyRaw [33]byte
acctKey, err := btcec.ParsePubKey(diff.TraderKey)
if err != nil {
return nil, fmt.Errorf("error parsing account key: %v",
err)
}
copy(acctKeyRaw[:], acctKey.SerializeCompressed())
b.AccountDiffs = append(
b.AccountDiffs, &AccountDiff{
AccountKeyRaw: acctKeyRaw,
AccountKey: acctKey,
EndingState: diff.EndingState,
EndingBalance: btcutil.Amount(diff.EndingBalance),
OutpointIndex: diff.OutpointIndex,
NewExpiry: diff.NewExpiry,
NewVersion: account.Version(diff.NewVersion),
},
)
}
// Parse batch transaction.
err := b.BatchTX.Deserialize(bytes.NewReader(
prepareMsg.BatchTransaction,
))
if err != nil {
return nil, fmt.Errorf("error parsing batch TX: %v", err)
}
// Convert clearing price, fee rate and rebate.
b.BatchTxFeeRate = chainfee.SatPerKWeight(prepareMsg.FeeRateSatPerKw)
b.FeeRebate = btcutil.Amount(prepareMsg.FeeRebateSat)
// Parse the execution fee.
if prepareMsg.ExecutionFee == nil {
return nil, fmt.Errorf("execution fee missing")
}
b.ExecutionFee = terms.NewLinearFeeSchedule(
btcutil.Amount(prepareMsg.ExecutionFee.BaseFee),
btcutil.Amount(prepareMsg.ExecutionFee.FeeRate),
)
// Parse the batch ID as public key just to make sure it's valid.
_, err = btcec.ParsePubKey(prepareMsg.BatchId)
if err != nil {
return nil, fmt.Errorf("error parsing batch ID: %v", err)
}
copy(b.ID[:], prepareMsg.BatchId)
return b, nil
}
// ParseRPCMatchedOrders parses the incoming raw RPC matched orders into the go
// native structs used by the order manager.
func ParseRPCMatchedOrders(orders *auctioneerrpc.MatchedOrder) ([]*MatchedOrder,
error) {
var result []*MatchedOrder
// The only thing we can check in this step is that not both matched
// bids and matched asks are set at the same time as that wouldn't make
// sense. Everything else is checked at a later stage when we know more
// about our order that was matched against.
switch {
case len(orders.MatchedAsks) > 0 && len(orders.MatchedBids) > 0:
return nil, fmt.Errorf("order cannot match both asks and bids")
case len(orders.MatchedAsks) > 0:
for _, ask := range orders.MatchedAsks {
matchedAsk, err := ParseRPCServerAsk(ask.Ask)
if err != nil {
return nil, fmt.Errorf("error parsing server "+
"ask: %v", err)
}
matchedAsk.UnitsFilled = SupplyUnit(ask.UnitsFilled)
result = append(result, matchedAsk)
}
case len(orders.MatchedBids) > 0:
for _, bid := range orders.MatchedBids {
matchedBid, err := ParseRPCServerBid(bid.Bid)
if err != nil {
return nil, fmt.Errorf("error parsing server "+
"bid: %v", err)
}
matchedBid.UnitsFilled = SupplyUnit(bid.UnitsFilled)
result = append(result, matchedBid)
}
}
return result, nil
}
// ParseRPCSign parses the incoming raw OrderMatchSignBegin into the go native
// structs used by the order manager.
func ParseRPCSign(signMsg *auctioneerrpc.OrderMatchSignBegin) (AccountNonces,
[]*wire.TxOut, error) {
nonces := make(AccountNonces, len(signMsg.ServerNonces))
for acctKeyHex, nonceBytes := range signMsg.ServerNonces {
var acctKey [btcec.PubKeyBytesLenCompressed]byte
if len(acctKeyHex) != hex.EncodedLen(len(acctKey)) {
return nil, nil, fmt.Errorf("invalid account key " +
"length in server nonces")
}
if len(nonceBytes) != musig2.PubNonceSize {
return nil, nil, fmt.Errorf("invalid pub nonce " +
"length in server nonces")
}
acctKeyBytes, err := hex.DecodeString(acctKeyHex)
if err != nil {
return nil, nil, fmt.Errorf("error hex decoding "+
"account key: %v", err)
}
copy(acctKey[:], acctKeyBytes)
var pubNonces [musig2.PubNonceSize]byte
copy(pubNonces[:], nonceBytes)
nonces[acctKey] = pubNonces
}
prevOutputs := make([]*wire.TxOut, len(signMsg.PrevOutputs))
for idx, rpcPrevOut := range signMsg.PrevOutputs {
prevOutputs[idx] = &wire.TxOut{
Value: int64(rpcPrevOut.Value),
PkScript: rpcPrevOut.PkScript,
}
}
return nonces, prevOutputs, nil
}
// MarshalNodeIDSlice returns a flattened version of an slice of node ids to be
// used in rpc serialization.
func MarshalNodeIDSlice(nodeIDs [][33]byte) [][]byte {
res := make([][]byte, 0, len(nodeIDs))
for i := range nodeIDs {
nodeID := make([]byte, 33)
copy(nodeID, nodeIDs[i][:])
res = append(res, nodeID)
}
return res
}
// UnmarshalNodeIDSlice returns a slice of node ids from a flatten version.
func UnmarshalNodeIDSlice(slice [][]byte) ([][33]byte, error) {
nodeIDs := make([][33]byte, len(slice))
for idx := range slice {
// Check that the node id pub key is in the correct format.
if len(slice[idx]) != 33 {
return nil, fmt.Errorf("invalid node_id length: %x",
slice[idx])
}
// Check that the node id pub key is a valid key.
if _, err := btcec.ParsePubKey(slice[idx]); err != nil {
return nil, fmt.Errorf("invalid node_id: %x",
slice[idx])
}
copy(nodeIDs[idx][:], slice[idx])
}
return nodeIDs, nil
}
// randomPreimage creates a new preimage from a random number generator.
func randomPreimage() ([]byte, error) {
var nonce Nonce
_, err := rand.Read(nonce[:])
if err != nil {
return nil, err
}
return nonce[:], nil
}