mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
Include https://github.com/lightninglabs/lndclient/pull/280 multi: migrate to btcd v2 modules + add WalletKit.SubmitPackage Migrate Loop imports and update Aperture and Taproot Assets to compatible revisions so this commit remains green on its own.
329 lines
8.3 KiB
Go
329 lines
8.3 KiB
Go
package loopdb
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
btcaddr "github.com/btcsuite/btcd/address/v2"
|
|
"github.com/btcsuite/btcd/btcutil/v2"
|
|
"github.com/btcsuite/btcd/chaincfg/v2"
|
|
"github.com/btcsuite/btcd/wire/v2"
|
|
)
|
|
|
|
// LoopOutContract contains the data that is serialized to persistent storage
|
|
// for pending swaps.
|
|
type LoopOutContract struct {
|
|
// SwapContract contains basic information pertaining to this swap.
|
|
// Each swap type has a base contract, then swap specific information
|
|
// on top of it.
|
|
SwapContract
|
|
|
|
// DestAddr is the destination address of the loop out swap.
|
|
DestAddr btcaddr.Address
|
|
|
|
// IsExternalAddr indicates whether the destination address does not
|
|
// belong to the backing lnd node.
|
|
IsExternalAddr bool
|
|
|
|
// SwapInvoice is the invoice that is to be paid by the client to
|
|
// initiate the loop out swap.
|
|
SwapInvoice string
|
|
|
|
// MaxSwapRoutingFee is the maximum off-chain fee in msat that may be
|
|
// paid for the swap payment to the server.
|
|
MaxSwapRoutingFee btcutil.Amount
|
|
|
|
// SweepConfTarget specifies the targeted confirmation target for the
|
|
// client sweep tx.
|
|
SweepConfTarget int32
|
|
|
|
// HtlcConfirmations is the number of confirmations we require the on
|
|
// chain htlc to have before proceeding with the swap.
|
|
HtlcConfirmations uint32
|
|
|
|
// OutgoingChanSet is the set of short ids of channels that may be used.
|
|
// If empty, any channel may be used.
|
|
OutgoingChanSet ChannelSet
|
|
|
|
// PrepayInvoice is the invoice that the client should pay to the
|
|
// server that will be returned if the swap is complete.
|
|
PrepayInvoice string
|
|
|
|
// MaxPrepayRoutingFee is the maximum off-chain fee in msat that may be
|
|
// paid for the prepayment to the server.
|
|
MaxPrepayRoutingFee btcutil.Amount
|
|
|
|
// SwapPublicationDeadline is a timestamp that the server commits to
|
|
// have the on-chain swap published by. It is set by the client to
|
|
// allow the server to delay the publication in exchange for possibly
|
|
// lower fees.
|
|
SwapPublicationDeadline time.Time
|
|
|
|
// PaymentTimeout is the timeout for any individual off-chain payment
|
|
// attempt.
|
|
PaymentTimeout time.Duration
|
|
|
|
// AssetSwapInfo contains information, should the loop out swpa be
|
|
// paid via an asset channel.
|
|
AssetSwapInfo *LoopOutAssetSwap
|
|
}
|
|
|
|
type LoopOutAssetSwap struct {
|
|
// AssetId is the optional asset id that is used to pay the swap invoice.
|
|
AssetId []byte
|
|
|
|
// PrepayRfqId is the rfq id that is used to pay the prepay invoice.
|
|
PrepayRfqId []byte
|
|
|
|
// SwapRfqId is the rfq id that is used to pay the swap invoice.
|
|
SwapRfqId []byte
|
|
|
|
// PrepayPaidAmt is the asset amount that was paid for the prepay
|
|
// invoice.
|
|
PrepayPaidAmt uint64
|
|
|
|
// SwapPaidAmt is the asset amount that was paid for the swap invoice.
|
|
SwapPaidAmt uint64
|
|
}
|
|
|
|
// ChannelSet stores a set of channels.
|
|
type ChannelSet []uint64
|
|
|
|
// String returns the human-readable representation of a channel set.
|
|
func (c ChannelSet) String() string {
|
|
channelStrings := make([]string, len(c))
|
|
for i, chanID := range c {
|
|
channelStrings[i] = strconv.FormatUint(chanID, 10)
|
|
}
|
|
return strings.Join(channelStrings, ",")
|
|
}
|
|
|
|
// NewChannelSet instantiates a new channel set and verifies that there are no
|
|
// duplicates present.
|
|
func NewChannelSet(set []uint64) (ChannelSet, error) {
|
|
// Check channel set for duplicates.
|
|
chanSet := make(map[uint64]struct{})
|
|
for _, chanID := range set {
|
|
if _, exists := chanSet[chanID]; exists {
|
|
return nil, fmt.Errorf("duplicate chan in set: id=%v",
|
|
chanID)
|
|
}
|
|
chanSet[chanID] = struct{}{}
|
|
}
|
|
|
|
return ChannelSet(set), nil
|
|
}
|
|
|
|
// LoopOut is a combination of the contract and the updates.
|
|
type LoopOut struct {
|
|
Loop
|
|
|
|
// Contract is the active contract for this swap. It describes the
|
|
// precise details of the swap including the final fee, CLTV value,
|
|
// etc.
|
|
Contract *LoopOutContract
|
|
}
|
|
|
|
// LastUpdateTime returns the last update time of this swap.
|
|
func (s *LoopOut) LastUpdateTime() time.Time {
|
|
lastUpdate := s.LastUpdate()
|
|
if lastUpdate == nil {
|
|
return s.Contract.InitiationTime
|
|
}
|
|
|
|
return lastUpdate.Time
|
|
}
|
|
|
|
func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) (
|
|
*LoopOutContract, error) {
|
|
|
|
r := bytes.NewReader(value)
|
|
|
|
contract := LoopOutContract{}
|
|
var err error
|
|
var unixNano int64
|
|
if err := binary.Read(r, byteOrder, &unixNano); err != nil {
|
|
return nil, err
|
|
}
|
|
contract.InitiationTime = time.Unix(0, unixNano)
|
|
|
|
if err := binary.Read(r, byteOrder, &contract.Preimage); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = binary.Read(r, byteOrder, &contract.AmountRequested)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
contract.PrepayInvoice, err = wire.ReadVarString(r, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
n, err := r.Read(contract.HtlcKeys.SenderScriptKey[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n != keyLength {
|
|
return nil, fmt.Errorf("sender key has invalid length")
|
|
}
|
|
|
|
n, err = r.Read(contract.HtlcKeys.ReceiverScriptKey[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n != keyLength {
|
|
return nil, fmt.Errorf("receiver key has invalid length")
|
|
}
|
|
|
|
if err := binary.Read(r, byteOrder, &contract.CltvExpiry); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := binary.Read(r, byteOrder, &contract.MaxMinerFee); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Read(r, byteOrder, &contract.MaxSwapFee); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Read(r, byteOrder, &contract.MaxPrepayRoutingFee); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := binary.Read(r, byteOrder, &contract.InitiationHeight); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
addr, err := wire.ReadVarString(r, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
contract.DestAddr, err = btcaddr.DecodeAddress(addr, chainParams)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
contract.SwapInvoice, err = wire.ReadVarString(r, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Read(r, byteOrder, &contract.SweepConfTarget); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Read(r, byteOrder, &contract.MaxSwapRoutingFee); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var unchargeChannel uint64
|
|
if err := binary.Read(r, byteOrder, &unchargeChannel); err != nil {
|
|
return nil, err
|
|
}
|
|
if unchargeChannel != 0 {
|
|
contract.OutgoingChanSet = ChannelSet{unchargeChannel}
|
|
}
|
|
|
|
var deadlineNano int64
|
|
err = binary.Read(r, byteOrder, &deadlineNano)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
contract.SwapPublicationDeadline = time.Unix(0, deadlineNano)
|
|
|
|
return &contract, nil
|
|
}
|
|
|
|
func serializeLoopOutContract(swap *LoopOutContract) (
|
|
[]byte, error) {
|
|
|
|
var b bytes.Buffer
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.InitiationTime.UnixNano()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.Preimage); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.AmountRequested); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := wire.WriteVarString(&b, 0, swap.PrepayInvoice); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
n, err := b.Write(swap.HtlcKeys.SenderScriptKey[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n != keyLength {
|
|
return nil, fmt.Errorf("sender key has invalid length")
|
|
}
|
|
|
|
n, err = b.Write(swap.HtlcKeys.ReceiverScriptKey[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n != keyLength {
|
|
return nil, fmt.Errorf("receiver key has invalid length")
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.CltvExpiry); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.MaxMinerFee); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.MaxSwapFee); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.MaxPrepayRoutingFee); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.InitiationHeight); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
addr := swap.DestAddr.String()
|
|
if err := wire.WriteVarString(&b, 0, addr); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := wire.WriteVarString(&b, 0, swap.SwapInvoice); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.SweepConfTarget); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.MaxSwapRoutingFee); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Always write no outgoing channel. This field is replaced by an
|
|
// outgoing channel set.
|
|
unchargeChannel := uint64(0)
|
|
if err := binary.Write(&b, byteOrder, unchargeChannel); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = binary.Write(&b, byteOrder, swap.SwapPublicationDeadline.UnixNano())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return b.Bytes(), nil
|
|
}
|