2019-03-07 02:19:57 +00:00
|
|
|
package loopdb
|
2019-03-06 20:13:50 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
2020-05-19 07:54:27 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2019-03-06 20:13:50 +00:00
|
|
|
"time"
|
|
|
|
|
2022-03-14 12:36:02 +00:00
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
2019-03-25 10:06:16 +00:00
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
2019-03-06 20:13:50 +00:00
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
|
|
)
|
|
|
|
|
2019-03-07 04:32:24 +00:00
|
|
|
// 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.
|
2019-03-07 02:19:57 +00:00
|
|
|
SwapContract
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-07 04:32:24 +00:00
|
|
|
// DestAddr is the destination address of the loop out swap.
|
2019-03-07 02:19:57 +00:00
|
|
|
DestAddr btcutil.Address
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2023-09-05 17:48:25 +00:00
|
|
|
// IsExternalAddr indicates whether the destination address does not
|
|
|
|
// belong to the backing lnd node.
|
|
|
|
IsExternalAddr bool
|
|
|
|
|
2019-03-07 04:32:24 +00:00
|
|
|
// SwapInvoice is the invoice that is to be paid by the client to
|
|
|
|
// initiate the loop out swap.
|
2019-03-07 02:19:57 +00:00
|
|
|
SwapInvoice string
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// MaxSwapRoutingFee is the maximum off-chain fee in msat that may be
|
|
|
|
// paid for the swap payment to the server.
|
|
|
|
MaxSwapRoutingFee btcutil.Amount
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// SweepConfTarget specifies the targeted confirmation target for the
|
|
|
|
// client sweep tx.
|
|
|
|
SweepConfTarget int32
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2020-08-04 18:28:06 +00:00
|
|
|
// HtlcConfirmations is the number of confirmations we require the on
|
|
|
|
// chain htlc to have before proceeding with the swap.
|
|
|
|
HtlcConfirmations uint32
|
|
|
|
|
2020-05-19 07:54:27 +00:00
|
|
|
// OutgoingChanSet is the set of short ids of channels that may be used.
|
|
|
|
// If empty, any channel may be used.
|
|
|
|
OutgoingChanSet ChannelSet
|
2019-03-12 15:09:57 +00:00
|
|
|
|
|
|
|
// 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
|
2019-11-14 09:35:31 +00:00
|
|
|
|
|
|
|
// 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
|
2024-05-09 14:36:10 +00:00
|
|
|
|
|
|
|
// PaymentTimeout is the timeout for any individual off-chain payment
|
|
|
|
// attempt.
|
|
|
|
PaymentTimeout time.Duration
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-19 07:54:27 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2019-03-07 04:32:24 +00:00
|
|
|
// LoopOut is a combination of the contract and the updates.
|
|
|
|
type LoopOut struct {
|
2019-03-12 15:09:57 +00:00
|
|
|
Loop
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// Contract is the active contract for this swap. It describes the
|
|
|
|
// precise details of the swap including the final fee, CLTV value,
|
|
|
|
// etc.
|
2019-03-07 04:32:24 +00:00
|
|
|
Contract *LoopOutContract
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// LastUpdateTime returns the last update time of this swap.
|
2019-03-07 04:32:24 +00:00
|
|
|
func (s *LoopOut) LastUpdateTime() time.Time {
|
2019-03-07 02:19:57 +00:00
|
|
|
lastUpdate := s.LastUpdate()
|
|
|
|
if lastUpdate == nil {
|
|
|
|
return s.Contract.InitiationTime
|
|
|
|
}
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
return lastUpdate.Time
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-25 10:06:16 +00:00
|
|
|
func deserializeLoopOutContract(value []byte, chainParams *chaincfg.Params) (
|
|
|
|
*LoopOutContract, error) {
|
|
|
|
|
2019-03-06 20:13:50 +00:00
|
|
|
r := bytes.NewReader(value)
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
contract := LoopOutContract{}
|
|
|
|
var err error
|
|
|
|
var unixNano int64
|
|
|
|
if err := binary.Read(r, byteOrder, &unixNano); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-12 15:09:57 +00:00
|
|
|
contract.InitiationTime = time.Unix(0, unixNano)
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Read(r, byteOrder, &contract.Preimage); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-07 15:29:24 +00:00
|
|
|
err = binary.Read(r, byteOrder, &contract.AmountRequested)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-12 15:09:57 +00:00
|
|
|
|
|
|
|
contract.PrepayInvoice, err = wire.ReadVarString(r, 0)
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-12 15:09:57 +00:00
|
|
|
|
2023-01-05 17:16:05 +00:00
|
|
|
n, err := r.Read(contract.HtlcKeys.SenderScriptKey[:])
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-12 15:09:57 +00:00
|
|
|
if n != keyLength {
|
|
|
|
return nil, fmt.Errorf("sender key has invalid length")
|
|
|
|
}
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2023-01-05 17:16:05 +00:00
|
|
|
n, err = r.Read(contract.HtlcKeys.ReceiverScriptKey[:])
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-12 15:09:57 +00:00
|
|
|
if n != keyLength {
|
|
|
|
return nil, fmt.Errorf("receiver key has invalid length")
|
|
|
|
}
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Read(r, byteOrder, &contract.CltvExpiry); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := binary.Read(r, byteOrder, &contract.MaxMinerFee); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Read(r, byteOrder, &contract.MaxSwapFee); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Read(r, byteOrder, &contract.MaxPrepayRoutingFee); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Read(r, byteOrder, &contract.InitiationHeight); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
addr, err := wire.ReadVarString(r, 0)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
contract.DestAddr, err = btcutil.DecodeAddress(addr, chainParams)
|
|
|
|
if err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
contract.SwapInvoice, err = wire.ReadVarString(r, 0)
|
|
|
|
if err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Read(r, byteOrder, &contract.SweepConfTarget); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Read(r, byteOrder, &contract.MaxSwapRoutingFee); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var unchargeChannel uint64
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Read(r, byteOrder, &unchargeChannel); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-12 15:09:57 +00:00
|
|
|
if unchargeChannel != 0 {
|
2020-05-19 07:54:27 +00:00
|
|
|
contract.OutgoingChanSet = ChannelSet{unchargeChannel}
|
2019-03-12 15:09:57 +00:00
|
|
|
}
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-11-14 09:35:31 +00:00
|
|
|
var deadlineNano int64
|
|
|
|
err = binary.Read(r, byteOrder, &deadlineNano)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
contract.SwapPublicationDeadline = time.Unix(0, deadlineNano)
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
return &contract, nil
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
func serializeLoopOutContract(swap *LoopOutContract) (
|
|
|
|
[]byte, error) {
|
|
|
|
|
|
|
|
var b bytes.Buffer
|
|
|
|
|
|
|
|
if err := binary.Write(&b, byteOrder, swap.InitiationTime.UnixNano()); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.Preimage); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.AmountRequested); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := wire.WriteVarString(&b, 0, swap.PrepayInvoice); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2023-01-05 17:16:05 +00:00
|
|
|
n, err := b.Write(swap.HtlcKeys.SenderScriptKey[:])
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if n != keyLength {
|
|
|
|
return nil, fmt.Errorf("sender key has invalid length")
|
|
|
|
}
|
|
|
|
|
2023-01-05 17:16:05 +00:00
|
|
|
n, err = b.Write(swap.HtlcKeys.ReceiverScriptKey[:])
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if n != keyLength {
|
|
|
|
return nil, fmt.Errorf("receiver key has invalid length")
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.CltvExpiry); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.MaxMinerFee); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.MaxSwapFee); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.MaxPrepayRoutingFee); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.InitiationHeight); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
addr := swap.DestAddr.String()
|
|
|
|
if err := wire.WriteVarString(&b, 0, addr); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := wire.WriteVarString(&b, 0, swap.SwapInvoice); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.SweepConfTarget); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, swap.MaxSwapRoutingFee); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2020-05-19 07:54:27 +00:00
|
|
|
// Always write no outgoing channel. This field is replaced by an
|
|
|
|
// outgoing channel set.
|
|
|
|
unchargeChannel := uint64(0)
|
2019-03-12 15:09:57 +00:00
|
|
|
if err := binary.Write(&b, byteOrder, unchargeChannel); err != nil {
|
|
|
|
return nil, err
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-11-14 09:35:31 +00:00
|
|
|
err = binary.Write(&b, byteOrder, swap.SwapPublicationDeadline.UnixNano())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
return b.Bytes(), nil
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|