You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
loop/loopdb/loopout.go

371 lines
8.4 KiB
Go

package loopdb
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"time"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/lntypes"
)
// SwapContract contains the base data that is serialized to persistent storage
// for pending swaps.
type SwapContract struct {
// Preimage is the preimage for the swap.
Preimage lntypes.Preimage
// AmountRequested is the total amount of the swap.
AmountRequested btcutil.Amount
// PrepayInvoice is the invoice that the client should pay to the
// server that will be returned if the swap is complete.
PrepayInvoice string
// SenderKey is the key of the sender that will be used in the on-chain
// HTLC.
SenderKey [33]byte
// ReceiverKey is the of the receiver that will be used in the on-chain
// HTLC.
ReceiverKey [33]byte
// CltvExpiry is the total absolute CLTV expiry of the swap.
CltvExpiry int32
// MaxPrepayRoutingFee is the maximum off-chain fee in msat that may be
// paid for the prepayment to the server.
MaxPrepayRoutingFee btcutil.Amount
// MaxSwapFee is the maximum we are willing to pay the server for the
// swap.
MaxSwapFee btcutil.Amount
// MaxMinerFee is the maximum in on-chain fees that we are willing to
// spend.
MaxMinerFee btcutil.Amount
// InitiationHeight is the block height at which the swap was
// initiated.
InitiationHeight int32
// InitiationTime is the time at which the swap was initiated.
InitiationTime time.Time
}
// 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 btcutil.Address
// 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
// TargetChannel is the channel to loop out. If zero, any channel may
// be used.
UnchargeChannel *uint64
}
// LoopOutEvent contains the dynamic data of a swap.
type LoopOutEvent struct {
// State is the new state for this swap as a result of this event.
State SwapState
// Time is the time that this swap had its state changed.
Time time.Time
}
// LoopOut is a combination of the contract and the updates.
type LoopOut struct {
// Hash is the hash that uniquely identifies this swap.
Hash lntypes.Hash
// 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
// Events are each of the state transitions that this swap underwent.
Events []*LoopOutEvent
}
// State returns the most recent state of this swap.
func (s *LoopOut) State() SwapState {
lastUpdate := s.LastUpdate()
if lastUpdate == nil {
return StateInitiated
}
return lastUpdate.State
}
// LastUpdate returns the most recent update of this swap.
func (s *LoopOut) LastUpdate() *LoopOutEvent {
eventCount := len(s.Events)
if eventCount == 0 {
return nil
}
lastEvent := s.Events[eventCount-1]
return lastEvent
}
// 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, err := deserializeContract(r)
if err != nil {
return nil, err
}
swap := LoopOutContract{
SwapContract: *contract,
}
addr, err := wire.ReadVarString(r, 0)
if err != nil {
return nil, err
}
swap.DestAddr, err = btcutil.DecodeAddress(addr, chainParams)
if err != nil {
return nil, err
}
swap.SwapInvoice, err = wire.ReadVarString(r, 0)
if err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &swap.SweepConfTarget); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &swap.MaxSwapRoutingFee); err != nil {
return nil, err
}
var unchargeChannel uint64
if err := binary.Read(r, byteOrder, &unchargeChannel); err != nil {
return nil, err
}
if unchargeChannel != 0 {
swap.UnchargeChannel = &unchargeChannel
}
return &swap, nil
}
func serializeLoopOutContract(swap *LoopOutContract) (
[]byte, error) {
var b bytes.Buffer
serializeContract(&swap.SwapContract, &b)
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
}
var unchargeChannel uint64
if swap.UnchargeChannel != nil {
unchargeChannel = *swap.UnchargeChannel
}
if err := binary.Write(&b, byteOrder, unchargeChannel); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func deserializeContract(r io.Reader) (*SwapContract, error) {
swap := SwapContract{}
var err error
var unixNano int64
if err := binary.Read(r, byteOrder, &unixNano); err != nil {
return nil, err
}
swap.InitiationTime = time.Unix(0, unixNano)
if err := binary.Read(r, byteOrder, &swap.Preimage); err != nil {
return nil, err
}
binary.Read(r, byteOrder, &swap.AmountRequested)
swap.PrepayInvoice, err = wire.ReadVarString(r, 0)
if err != nil {
return nil, err
}
n, err := r.Read(swap.SenderKey[:])
if err != nil {
return nil, err
}
if n != keyLength {
return nil, fmt.Errorf("sender key has invalid length")
}
n, err = r.Read(swap.ReceiverKey[:])
if err != nil {
return nil, err
}
if n != keyLength {
return nil, fmt.Errorf("receiver key has invalid length")
}
if err := binary.Read(r, byteOrder, &swap.CltvExpiry); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &swap.MaxMinerFee); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &swap.MaxSwapFee); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &swap.MaxPrepayRoutingFee); err != nil {
return nil, err
}
if err := binary.Read(r, byteOrder, &swap.InitiationHeight); err != nil {
return nil, err
}
return &swap, nil
}
func serializeContract(swap *SwapContract, b *bytes.Buffer) error {
if err := binary.Write(b, byteOrder, swap.InitiationTime.UnixNano()); err != nil {
return err
}
if err := binary.Write(b, byteOrder, swap.Preimage); err != nil {
return err
}
if err := binary.Write(b, byteOrder, swap.AmountRequested); err != nil {
return err
}
if err := wire.WriteVarString(b, 0, swap.PrepayInvoice); err != nil {
return err
}
n, err := b.Write(swap.SenderKey[:])
if err != nil {
return err
}
if n != keyLength {
return fmt.Errorf("sender key has invalid length")
}
n, err = b.Write(swap.ReceiverKey[:])
if err != nil {
return err
}
if n != keyLength {
return fmt.Errorf("receiver key has invalid length")
}
if err := binary.Write(b, byteOrder, swap.CltvExpiry); err != nil {
return err
}
if err := binary.Write(b, byteOrder, swap.MaxMinerFee); err != nil {
return err
}
if err := binary.Write(b, byteOrder, swap.MaxSwapFee); err != nil {
return err
}
if err := binary.Write(b, byteOrder, swap.MaxPrepayRoutingFee); err != nil {
return err
}
if err := binary.Write(b, byteOrder, swap.InitiationHeight); err != nil {
return err
}
return nil
}
func serializeLoopOutEvent(time time.Time, state SwapState) (
[]byte, error) {
var b bytes.Buffer
if err := binary.Write(&b, byteOrder, time.UnixNano()); err != nil {
return nil, err
}
if err := binary.Write(&b, byteOrder, state); err != nil {
return nil, err
}
return b.Bytes(), nil
}
func deserializeLoopOutEvent(value []byte) (*LoopOutEvent, error) {
update := &LoopOutEvent{}
r := bytes.NewReader(value)
var unixNano int64
if err := binary.Read(r, byteOrder, &unixNano); err != nil {
return nil, err
}
update.Time = time.Unix(0, unixNano)
if err := binary.Read(r, byteOrder, &update.State); err != nil {
return nil, err
}
return update, nil
}