pool/order/rpc_parse.go
Oliver Gugger 2a790a1884
multi: add self chan balance to order submission RPCs
With everything prepared we now parse the self chan balance from the
client request and set it on the server request.
2021-03-19 16:50:31 +01:00

380 lines
10 KiB
Go

package order
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"net"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"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"
)
// 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) (*Kit, error) {
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.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 details.MinUnitsMatch > uint32(kit.Units):
return nil, errors.New("min units match must not exceed " +
"total order units")
}
kit.MinUnitsMatch = SupplyUnit(details.MinUnitsMatch)
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.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, btcec.S256())
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, btcec.S256(),
)
if err != nil {
return nil, nodeKey, nodeAddrs, multiSigKey,
fmt.Errorf("unable to parse multi sig pub key: %v", err)
}
copy(multiSigKey[:], multiSigPubkey.SerializeCompressed())
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
o.Order = &Ask{
Kit: *kit,
}
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),
}
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),
}
// 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, btcec.S256())
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,
},
)
}
// 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, btcec.S256())
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
}
// 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
}