mirror of https://github.com/lightninglabs/loop
instantout: add instantout store
parent
c6da670a4f
commit
22de4e0556
@ -0,0 +1,432 @@
|
|||||||
|
package instantout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg"
|
||||||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
"github.com/lightninglabs/loop/fsm"
|
||||||
|
"github.com/lightninglabs/loop/instantout/reservation"
|
||||||
|
"github.com/lightninglabs/loop/loopdb"
|
||||||
|
"github.com/lightninglabs/loop/loopdb/sqlc"
|
||||||
|
"github.com/lightningnetwork/lnd/clock"
|
||||||
|
"github.com/lightningnetwork/lnd/keychain"
|
||||||
|
"github.com/lightningnetwork/lnd/lntypes"
|
||||||
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstantOutBaseDB is the interface that contains all the queries generated
|
||||||
|
// by sqlc for the instantout table.
|
||||||
|
type InstantOutBaseDB interface {
|
||||||
|
// InsertSwap inserts a new base swap.
|
||||||
|
InsertSwap(ctx context.Context, arg sqlc.InsertSwapParams) error
|
||||||
|
|
||||||
|
// InsertHtlcKeys inserts the htlc keys for a swap.
|
||||||
|
InsertHtlcKeys(ctx context.Context, arg sqlc.InsertHtlcKeysParams) error
|
||||||
|
|
||||||
|
// InsertInstantOut inserts a new instant out swap.
|
||||||
|
InsertInstantOut(ctx context.Context,
|
||||||
|
arg sqlc.InsertInstantOutParams) error
|
||||||
|
|
||||||
|
// InsertInstantOutUpdate inserts a new instant out update.
|
||||||
|
InsertInstantOutUpdate(ctx context.Context,
|
||||||
|
arg sqlc.InsertInstantOutUpdateParams) error
|
||||||
|
|
||||||
|
// UpdateInstantOut updates an instant out swap.
|
||||||
|
UpdateInstantOut(ctx context.Context,
|
||||||
|
arg sqlc.UpdateInstantOutParams) error
|
||||||
|
|
||||||
|
// GetInstantOutSwap retrieves an instant out swap.
|
||||||
|
GetInstantOutSwap(ctx context.Context,
|
||||||
|
swapHash []byte) (sqlc.GetInstantOutSwapRow, error)
|
||||||
|
|
||||||
|
// GetInstantOutSwapUpdates retrieves all instant out swap updates.
|
||||||
|
GetInstantOutSwapUpdates(ctx context.Context,
|
||||||
|
swapHash []byte) ([]sqlc.InstantoutUpdate, error)
|
||||||
|
|
||||||
|
// GetInstantOutSwaps retrieves all instant out swaps.
|
||||||
|
GetInstantOutSwaps(ctx context.Context) ([]sqlc.GetInstantOutSwapsRow,
|
||||||
|
error)
|
||||||
|
|
||||||
|
// ExecTx allows for executing a function in the context of a database
|
||||||
|
// transaction.
|
||||||
|
ExecTx(ctx context.Context, txOptions loopdb.TxOptions,
|
||||||
|
txBody func(*sqlc.Queries) error) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReservationStore is the interface that is required to load the reservations
|
||||||
|
// based on the stored reservation ids.
|
||||||
|
type ReservationStore interface {
|
||||||
|
// GetReservation returns the reservation for the given id.
|
||||||
|
GetReservation(ctx context.Context, id reservation.ID) (
|
||||||
|
*reservation.Reservation, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SQLStore struct {
|
||||||
|
baseDb InstantOutBaseDB
|
||||||
|
reservationStore ReservationStore
|
||||||
|
clock clock.Clock
|
||||||
|
network *chaincfg.Params
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSQLStore creates a new SQLStore.
|
||||||
|
func NewSQLStore(db InstantOutBaseDB, clock clock.Clock,
|
||||||
|
reservationStore ReservationStore, network *chaincfg.Params) *SQLStore {
|
||||||
|
|
||||||
|
return &SQLStore{
|
||||||
|
baseDb: db,
|
||||||
|
clock: clock,
|
||||||
|
reservationStore: reservationStore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInstantLoopOut adds a new instant loop out to the store.
|
||||||
|
func (s *SQLStore) CreateInstantLoopOut(ctx context.Context,
|
||||||
|
instantOut *InstantOut) error {
|
||||||
|
|
||||||
|
swapArgs := sqlc.InsertSwapParams{
|
||||||
|
SwapHash: instantOut.SwapHash[:],
|
||||||
|
Preimage: instantOut.SwapPreimage[:],
|
||||||
|
InitiationTime: s.clock.Now(),
|
||||||
|
AmountRequested: int64(instantOut.Value),
|
||||||
|
CltvExpiry: instantOut.CltvExpiry,
|
||||||
|
MaxMinerFee: 0,
|
||||||
|
MaxSwapFee: 0,
|
||||||
|
InitiationHeight: instantOut.InitiationHeight,
|
||||||
|
ProtocolVersion: int32(instantOut.ProtocolVersion),
|
||||||
|
Label: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
htlcKeyArgs := sqlc.InsertHtlcKeysParams{
|
||||||
|
SwapHash: instantOut.SwapHash[:],
|
||||||
|
SenderScriptPubkey: instantOut.ServerPubkey.
|
||||||
|
SerializeCompressed(),
|
||||||
|
ReceiverScriptPubkey: instantOut.ClientPubkey.
|
||||||
|
SerializeCompressed(),
|
||||||
|
ClientKeyFamily: int32(instantOut.KeyLocator.Family),
|
||||||
|
ClientKeyIndex: int32(instantOut.KeyLocator.Index),
|
||||||
|
}
|
||||||
|
|
||||||
|
reservationIdByteSlice := reservationIdsToByteSlice(
|
||||||
|
instantOut.Reservations,
|
||||||
|
)
|
||||||
|
instantOutArgs := sqlc.InsertInstantOutParams{
|
||||||
|
SwapHash: instantOut.SwapHash[:],
|
||||||
|
Preimage: instantOut.SwapPreimage[:],
|
||||||
|
SweepAddress: instantOut.SweepAddress.String(),
|
||||||
|
OutgoingChanSet: instantOut.OutgoingChanSet.String(),
|
||||||
|
HtlcFeeRate: int64(instantOut.HtlcFeeRate),
|
||||||
|
ReservationIds: reservationIdByteSlice,
|
||||||
|
SwapInvoice: instantOut.SwapInvoice,
|
||||||
|
}
|
||||||
|
|
||||||
|
updateArgs := sqlc.InsertInstantOutUpdateParams{
|
||||||
|
SwapHash: instantOut.SwapHash[:],
|
||||||
|
UpdateTimestamp: s.clock.Now(),
|
||||||
|
UpdateState: string(instantOut.State),
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.baseDb.ExecTx(ctx, &loopdb.SqliteTxOptions{},
|
||||||
|
func(q *sqlc.Queries) error {
|
||||||
|
err := q.InsertSwap(ctx, swapArgs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.InsertHtlcKeys(ctx, htlcKeyArgs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = q.InsertInstantOut(ctx, instantOutArgs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.InsertInstantOutUpdate(ctx, updateArgs)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateInstantLoopOut updates an existing instant loop out in the
|
||||||
|
// store.
|
||||||
|
func (s *SQLStore) UpdateInstantLoopOut(ctx context.Context,
|
||||||
|
instantOut *InstantOut) error {
|
||||||
|
|
||||||
|
// Serialize the FinalHtlcTx.
|
||||||
|
var finalHtlcTx []byte
|
||||||
|
if instantOut.FinalizedHtlcTx != nil {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
err := instantOut.FinalizedHtlcTx.Serialize(
|
||||||
|
&buffer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
finalHtlcTx = buffer.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
var finalSweeplessSweepTx []byte
|
||||||
|
if instantOut.FinalizedSweeplessSweepTx != nil {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
err := instantOut.FinalizedSweeplessSweepTx.Serialize(
|
||||||
|
&buffer,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
finalSweeplessSweepTx = buffer.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
var sweepTxHash []byte
|
||||||
|
if instantOut.SweepTxHash != nil {
|
||||||
|
sweepTxHash = instantOut.SweepTxHash[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParams := sqlc.UpdateInstantOutParams{
|
||||||
|
SwapHash: instantOut.SwapHash[:],
|
||||||
|
FinalizedHtlcTx: finalHtlcTx,
|
||||||
|
SweepTxHash: sweepTxHash,
|
||||||
|
FinalizedSweeplessSweepTx: finalSweeplessSweepTx,
|
||||||
|
SweepConfirmationHeight: serializeNullInt32(
|
||||||
|
int32(instantOut.SweepConfirmationHeight),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
updateArgs := sqlc.InsertInstantOutUpdateParams{
|
||||||
|
SwapHash: instantOut.SwapHash[:],
|
||||||
|
UpdateTimestamp: s.clock.Now(),
|
||||||
|
UpdateState: string(instantOut.State),
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.baseDb.ExecTx(ctx, &loopdb.SqliteTxOptions{},
|
||||||
|
func(q *sqlc.Queries) error {
|
||||||
|
err := q.UpdateInstantOut(ctx, updateParams)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return q.InsertInstantOutUpdate(ctx, updateArgs)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstantLoopOut returns the instant loop out for the given swap
|
||||||
|
// hash.
|
||||||
|
func (s *SQLStore) GetInstantLoopOut(ctx context.Context, swapHash []byte) (
|
||||||
|
*InstantOut, error) {
|
||||||
|
|
||||||
|
row, err := s.baseDb.GetInstantOutSwap(ctx, swapHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
updates, err := s.baseDb.GetInstantOutSwapUpdates(ctx, swapHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.sqlInstantOutToInstantOut(ctx, row, updates)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListInstantLoopOuts returns all instant loop outs that are in the
|
||||||
|
// store.
|
||||||
|
func (s *SQLStore) ListInstantLoopOuts(ctx context.Context) ([]*InstantOut,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
rows, err := s.baseDb.GetInstantOutSwaps(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var instantOuts []*InstantOut
|
||||||
|
for _, row := range rows {
|
||||||
|
updates, err := s.baseDb.GetInstantOutSwapUpdates(
|
||||||
|
ctx, row.SwapHash,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instantOut, err := s.sqlInstantOutToInstantOut(
|
||||||
|
ctx, sqlc.GetInstantOutSwapRow(row), updates,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instantOuts = append(instantOuts, instantOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
return instantOuts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sqlInstantOutToInstantOut converts sql rows to an instant out struct.
|
||||||
|
func (s *SQLStore) sqlInstantOutToInstantOut(ctx context.Context,
|
||||||
|
row sqlc.GetInstantOutSwapRow, updates []sqlc.InstantoutUpdate) (
|
||||||
|
*InstantOut, error) {
|
||||||
|
|
||||||
|
swapHash, err := lntypes.MakeHash(row.SwapHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
swapPreImage, err := lntypes.MakePreimage(row.Preimage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverKey, err := btcec.ParsePubKey(row.SenderScriptPubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
clientKey, err := btcec.ParsePubKey(row.ReceiverScriptPubkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var finalizedHtlcTx *wire.MsgTx
|
||||||
|
if row.FinalizedHtlcTx != nil {
|
||||||
|
finalizedHtlcTx = &wire.MsgTx{}
|
||||||
|
err := finalizedHtlcTx.Deserialize(bytes.NewReader(
|
||||||
|
row.FinalizedHtlcTx,
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var finalizedSweepLessSweepTx *wire.MsgTx
|
||||||
|
if row.FinalizedSweeplessSweepTx != nil {
|
||||||
|
finalizedSweepLessSweepTx = &wire.MsgTx{}
|
||||||
|
err := finalizedSweepLessSweepTx.Deserialize(bytes.NewReader(
|
||||||
|
row.FinalizedSweeplessSweepTx,
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sweepTxHash *chainhash.Hash
|
||||||
|
if row.SweepTxHash != nil {
|
||||||
|
sweepTxHash, err = chainhash.NewHash(row.SweepTxHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var outgoingChanSet loopdb.ChannelSet
|
||||||
|
if row.OutgoingChanSet != "" {
|
||||||
|
outgoingChanSet, err = loopdb.ConvertOutgoingChanSet(
|
||||||
|
row.OutgoingChanSet,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reservationIds, err := byteSliceToReservationIds(row.ReservationIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reservations := make([]*reservation.Reservation, 0, len(reservationIds))
|
||||||
|
for _, id := range reservationIds {
|
||||||
|
reservation, err := s.reservationStore.GetReservation(
|
||||||
|
ctx, id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
reservations = append(reservations, reservation)
|
||||||
|
}
|
||||||
|
|
||||||
|
sweepAddress, err := btcutil.DecodeAddress(row.SweepAddress, s.network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
instantOut := &InstantOut{
|
||||||
|
SwapHash: swapHash,
|
||||||
|
SwapPreimage: swapPreImage,
|
||||||
|
CltvExpiry: row.CltvExpiry,
|
||||||
|
OutgoingChanSet: outgoingChanSet,
|
||||||
|
Reservations: reservations,
|
||||||
|
ProtocolVersion: ProtocolVersion(row.ProtocolVersion),
|
||||||
|
InitiationHeight: row.InitiationHeight,
|
||||||
|
Value: btcutil.Amount(row.AmountRequested),
|
||||||
|
KeyLocator: keychain.KeyLocator{
|
||||||
|
Family: keychain.KeyFamily(row.ClientKeyFamily),
|
||||||
|
Index: uint32(row.ClientKeyIndex),
|
||||||
|
},
|
||||||
|
ClientPubkey: clientKey,
|
||||||
|
ServerPubkey: serverKey,
|
||||||
|
SwapInvoice: row.SwapInvoice,
|
||||||
|
HtlcFeeRate: chainfee.SatPerKWeight(row.HtlcFeeRate),
|
||||||
|
SweepAddress: sweepAddress,
|
||||||
|
FinalizedHtlcTx: finalizedHtlcTx,
|
||||||
|
SweepTxHash: sweepTxHash,
|
||||||
|
FinalizedSweeplessSweepTx: finalizedSweepLessSweepTx,
|
||||||
|
SweepConfirmationHeight: uint32(deserializeNullInt32(
|
||||||
|
row.SweepConfirmationHeight,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(updates) > 0 {
|
||||||
|
lastUpdate := updates[len(updates)-1]
|
||||||
|
instantOut.State = fsm.StateType(lastUpdate.UpdateState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return instantOut, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reservationIdsToByteSlice converts a slice of reservation ids to a byte
|
||||||
|
// slice.
|
||||||
|
func reservationIdsToByteSlice(reservations []*reservation.Reservation) []byte {
|
||||||
|
var reservationIds []byte
|
||||||
|
for _, reservation := range reservations {
|
||||||
|
reservationIds = append(reservationIds, reservation.ID[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reservationIds
|
||||||
|
}
|
||||||
|
|
||||||
|
// byteSliceToReservationIds converts a byte slice to a slice of reservation
|
||||||
|
// ids.
|
||||||
|
func byteSliceToReservationIds(byteSlice []byte) ([]reservation.ID, error) {
|
||||||
|
if len(byteSlice)%32 != 0 {
|
||||||
|
return nil, fmt.Errorf("invalid byte slice length")
|
||||||
|
}
|
||||||
|
|
||||||
|
var reservationIds []reservation.ID
|
||||||
|
for i := 0; i < len(byteSlice); i += 32 {
|
||||||
|
var id reservation.ID
|
||||||
|
copy(id[:], byteSlice[i:i+32])
|
||||||
|
reservationIds = append(reservationIds, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reservationIds, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializeNullInt32 serializes an int32 to a sql.NullInt32.
|
||||||
|
func serializeNullInt32(i int32) sql.NullInt32 {
|
||||||
|
return sql.NullInt32{
|
||||||
|
Int32: i,
|
||||||
|
Valid: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deserializeNullInt32 deserializes an int32 from a sql.NullInt32.
|
||||||
|
func deserializeNullInt32(i sql.NullInt32) int32 {
|
||||||
|
if i.Valid {
|
||||||
|
return i.Int32
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package instantout
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/lightninglabs/loop/instantout/reservation"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConvertingReservations(t *testing.T) {
|
||||||
|
var resId1, resId2 reservation.ID
|
||||||
|
|
||||||
|
// fill the ids with random values.
|
||||||
|
if _, err := rand.Read(resId1[:]); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := rand.Read(resId2[:]); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
reservations := []*reservation.Reservation{
|
||||||
|
{ID: resId1}, {ID: resId2},
|
||||||
|
}
|
||||||
|
|
||||||
|
byteSlice := reservationIdsToByteSlice(reservations)
|
||||||
|
require.Len(t, byteSlice, 64)
|
||||||
|
|
||||||
|
reservationIds, err := byteSliceToReservationIds(byteSlice)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Len(t, reservationIds, 2)
|
||||||
|
require.Equal(t, resId1, reservationIds[0])
|
||||||
|
require.Equal(t, resId2, reservationIds[1])
|
||||||
|
}
|
@ -0,0 +1,329 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.17.2
|
||||||
|
// source: instantout.sql
|
||||||
|
|
||||||
|
package sqlc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const getInstantOutSwap = `-- name: GetInstantOutSwap :one
|
||||||
|
SELECT
|
||||||
|
swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label,
|
||||||
|
instantout_swaps.swap_hash, instantout_swaps.preimage, instantout_swaps.sweep_address, instantout_swaps.outgoing_chan_set, instantout_swaps.htlc_fee_rate, instantout_swaps.reservation_ids, instantout_swaps.swap_invoice, instantout_swaps.finalized_htlc_tx, instantout_swaps.sweep_tx_hash, instantout_swaps.finalized_sweepless_sweep_tx, instantout_swaps.sweep_confirmation_height,
|
||||||
|
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index
|
||||||
|
FROM
|
||||||
|
swaps
|
||||||
|
JOIN
|
||||||
|
instantout_swaps ON swaps.swap_hash = instantout_swaps.swap_hash
|
||||||
|
JOIN
|
||||||
|
htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash
|
||||||
|
WHERE
|
||||||
|
swaps.swap_hash = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetInstantOutSwapRow struct {
|
||||||
|
ID int32
|
||||||
|
SwapHash []byte
|
||||||
|
Preimage []byte
|
||||||
|
InitiationTime time.Time
|
||||||
|
AmountRequested int64
|
||||||
|
CltvExpiry int32
|
||||||
|
MaxMinerFee int64
|
||||||
|
MaxSwapFee int64
|
||||||
|
InitiationHeight int32
|
||||||
|
ProtocolVersion int32
|
||||||
|
Label string
|
||||||
|
SwapHash_2 []byte
|
||||||
|
Preimage_2 []byte
|
||||||
|
SweepAddress string
|
||||||
|
OutgoingChanSet string
|
||||||
|
HtlcFeeRate int64
|
||||||
|
ReservationIds []byte
|
||||||
|
SwapInvoice string
|
||||||
|
FinalizedHtlcTx []byte
|
||||||
|
SweepTxHash []byte
|
||||||
|
FinalizedSweeplessSweepTx []byte
|
||||||
|
SweepConfirmationHeight sql.NullInt32
|
||||||
|
SwapHash_3 []byte
|
||||||
|
SenderScriptPubkey []byte
|
||||||
|
ReceiverScriptPubkey []byte
|
||||||
|
SenderInternalPubkey []byte
|
||||||
|
ReceiverInternalPubkey []byte
|
||||||
|
ClientKeyFamily int32
|
||||||
|
ClientKeyIndex int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetInstantOutSwap(ctx context.Context, swapHash []byte) (GetInstantOutSwapRow, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getInstantOutSwap, swapHash)
|
||||||
|
var i GetInstantOutSwapRow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.SwapHash,
|
||||||
|
&i.Preimage,
|
||||||
|
&i.InitiationTime,
|
||||||
|
&i.AmountRequested,
|
||||||
|
&i.CltvExpiry,
|
||||||
|
&i.MaxMinerFee,
|
||||||
|
&i.MaxSwapFee,
|
||||||
|
&i.InitiationHeight,
|
||||||
|
&i.ProtocolVersion,
|
||||||
|
&i.Label,
|
||||||
|
&i.SwapHash_2,
|
||||||
|
&i.Preimage_2,
|
||||||
|
&i.SweepAddress,
|
||||||
|
&i.OutgoingChanSet,
|
||||||
|
&i.HtlcFeeRate,
|
||||||
|
&i.ReservationIds,
|
||||||
|
&i.SwapInvoice,
|
||||||
|
&i.FinalizedHtlcTx,
|
||||||
|
&i.SweepTxHash,
|
||||||
|
&i.FinalizedSweeplessSweepTx,
|
||||||
|
&i.SweepConfirmationHeight,
|
||||||
|
&i.SwapHash_3,
|
||||||
|
&i.SenderScriptPubkey,
|
||||||
|
&i.ReceiverScriptPubkey,
|
||||||
|
&i.SenderInternalPubkey,
|
||||||
|
&i.ReceiverInternalPubkey,
|
||||||
|
&i.ClientKeyFamily,
|
||||||
|
&i.ClientKeyIndex,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getInstantOutSwapUpdates = `-- name: GetInstantOutSwapUpdates :many
|
||||||
|
SELECT
|
||||||
|
instantout_updates.id, instantout_updates.swap_hash, instantout_updates.update_state, instantout_updates.update_timestamp
|
||||||
|
FROM
|
||||||
|
instantout_updates
|
||||||
|
WHERE
|
||||||
|
instantout_updates.swap_hash = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetInstantOutSwapUpdates(ctx context.Context, swapHash []byte) ([]InstantoutUpdate, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getInstantOutSwapUpdates, swapHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []InstantoutUpdate
|
||||||
|
for rows.Next() {
|
||||||
|
var i InstantoutUpdate
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.SwapHash,
|
||||||
|
&i.UpdateState,
|
||||||
|
&i.UpdateTimestamp,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const getInstantOutSwaps = `-- name: GetInstantOutSwaps :many
|
||||||
|
SELECT
|
||||||
|
swaps.id, swaps.swap_hash, swaps.preimage, swaps.initiation_time, swaps.amount_requested, swaps.cltv_expiry, swaps.max_miner_fee, swaps.max_swap_fee, swaps.initiation_height, swaps.protocol_version, swaps.label,
|
||||||
|
instantout_swaps.swap_hash, instantout_swaps.preimage, instantout_swaps.sweep_address, instantout_swaps.outgoing_chan_set, instantout_swaps.htlc_fee_rate, instantout_swaps.reservation_ids, instantout_swaps.swap_invoice, instantout_swaps.finalized_htlc_tx, instantout_swaps.sweep_tx_hash, instantout_swaps.finalized_sweepless_sweep_tx, instantout_swaps.sweep_confirmation_height,
|
||||||
|
htlc_keys.swap_hash, htlc_keys.sender_script_pubkey, htlc_keys.receiver_script_pubkey, htlc_keys.sender_internal_pubkey, htlc_keys.receiver_internal_pubkey, htlc_keys.client_key_family, htlc_keys.client_key_index
|
||||||
|
FROM
|
||||||
|
swaps
|
||||||
|
JOIN
|
||||||
|
instantout_swaps ON swaps.swap_hash = instantout_swaps.swap_hash
|
||||||
|
JOIN
|
||||||
|
htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash
|
||||||
|
ORDER BY
|
||||||
|
swaps.id
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetInstantOutSwapsRow struct {
|
||||||
|
ID int32
|
||||||
|
SwapHash []byte
|
||||||
|
Preimage []byte
|
||||||
|
InitiationTime time.Time
|
||||||
|
AmountRequested int64
|
||||||
|
CltvExpiry int32
|
||||||
|
MaxMinerFee int64
|
||||||
|
MaxSwapFee int64
|
||||||
|
InitiationHeight int32
|
||||||
|
ProtocolVersion int32
|
||||||
|
Label string
|
||||||
|
SwapHash_2 []byte
|
||||||
|
Preimage_2 []byte
|
||||||
|
SweepAddress string
|
||||||
|
OutgoingChanSet string
|
||||||
|
HtlcFeeRate int64
|
||||||
|
ReservationIds []byte
|
||||||
|
SwapInvoice string
|
||||||
|
FinalizedHtlcTx []byte
|
||||||
|
SweepTxHash []byte
|
||||||
|
FinalizedSweeplessSweepTx []byte
|
||||||
|
SweepConfirmationHeight sql.NullInt32
|
||||||
|
SwapHash_3 []byte
|
||||||
|
SenderScriptPubkey []byte
|
||||||
|
ReceiverScriptPubkey []byte
|
||||||
|
SenderInternalPubkey []byte
|
||||||
|
ReceiverInternalPubkey []byte
|
||||||
|
ClientKeyFamily int32
|
||||||
|
ClientKeyIndex int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetInstantOutSwaps(ctx context.Context) ([]GetInstantOutSwapsRow, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getInstantOutSwaps)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetInstantOutSwapsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetInstantOutSwapsRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.SwapHash,
|
||||||
|
&i.Preimage,
|
||||||
|
&i.InitiationTime,
|
||||||
|
&i.AmountRequested,
|
||||||
|
&i.CltvExpiry,
|
||||||
|
&i.MaxMinerFee,
|
||||||
|
&i.MaxSwapFee,
|
||||||
|
&i.InitiationHeight,
|
||||||
|
&i.ProtocolVersion,
|
||||||
|
&i.Label,
|
||||||
|
&i.SwapHash_2,
|
||||||
|
&i.Preimage_2,
|
||||||
|
&i.SweepAddress,
|
||||||
|
&i.OutgoingChanSet,
|
||||||
|
&i.HtlcFeeRate,
|
||||||
|
&i.ReservationIds,
|
||||||
|
&i.SwapInvoice,
|
||||||
|
&i.FinalizedHtlcTx,
|
||||||
|
&i.SweepTxHash,
|
||||||
|
&i.FinalizedSweeplessSweepTx,
|
||||||
|
&i.SweepConfirmationHeight,
|
||||||
|
&i.SwapHash_3,
|
||||||
|
&i.SenderScriptPubkey,
|
||||||
|
&i.ReceiverScriptPubkey,
|
||||||
|
&i.SenderInternalPubkey,
|
||||||
|
&i.ReceiverInternalPubkey,
|
||||||
|
&i.ClientKeyFamily,
|
||||||
|
&i.ClientKeyIndex,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertInstantOut = `-- name: InsertInstantOut :exec
|
||||||
|
INSERT INTO instantout_swaps (
|
||||||
|
swap_hash,
|
||||||
|
preimage,
|
||||||
|
sweep_address,
|
||||||
|
outgoing_chan_set,
|
||||||
|
htlc_fee_rate,
|
||||||
|
reservation_ids,
|
||||||
|
swap_invoice
|
||||||
|
) VALUES (
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3,
|
||||||
|
$4,
|
||||||
|
$5,
|
||||||
|
$6,
|
||||||
|
$7
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertInstantOutParams struct {
|
||||||
|
SwapHash []byte
|
||||||
|
Preimage []byte
|
||||||
|
SweepAddress string
|
||||||
|
OutgoingChanSet string
|
||||||
|
HtlcFeeRate int64
|
||||||
|
ReservationIds []byte
|
||||||
|
SwapInvoice string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertInstantOut(ctx context.Context, arg InsertInstantOutParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, insertInstantOut,
|
||||||
|
arg.SwapHash,
|
||||||
|
arg.Preimage,
|
||||||
|
arg.SweepAddress,
|
||||||
|
arg.OutgoingChanSet,
|
||||||
|
arg.HtlcFeeRate,
|
||||||
|
arg.ReservationIds,
|
||||||
|
arg.SwapInvoice,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertInstantOutUpdate = `-- name: InsertInstantOutUpdate :exec
|
||||||
|
INSERT INTO instantout_updates (
|
||||||
|
swap_hash,
|
||||||
|
update_state,
|
||||||
|
update_timestamp
|
||||||
|
) VALUES (
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertInstantOutUpdateParams struct {
|
||||||
|
SwapHash []byte
|
||||||
|
UpdateState string
|
||||||
|
UpdateTimestamp time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertInstantOutUpdate(ctx context.Context, arg InsertInstantOutUpdateParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, insertInstantOutUpdate, arg.SwapHash, arg.UpdateState, arg.UpdateTimestamp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateInstantOut = `-- name: UpdateInstantOut :exec
|
||||||
|
UPDATE instantout_swaps
|
||||||
|
SET
|
||||||
|
finalized_htlc_tx = $2,
|
||||||
|
sweep_tx_hash = $3,
|
||||||
|
finalized_sweepless_sweep_tx = $4,
|
||||||
|
sweep_confirmation_height = $5
|
||||||
|
WHERE
|
||||||
|
instantout_swaps.swap_hash = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateInstantOutParams struct {
|
||||||
|
SwapHash []byte
|
||||||
|
FinalizedHtlcTx []byte
|
||||||
|
SweepTxHash []byte
|
||||||
|
FinalizedSweeplessSweepTx []byte
|
||||||
|
SweepConfirmationHeight sql.NullInt32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateInstantOut(ctx context.Context, arg UpdateInstantOutParams) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, updateInstantOut,
|
||||||
|
arg.SwapHash,
|
||||||
|
arg.FinalizedHtlcTx,
|
||||||
|
arg.SweepTxHash,
|
||||||
|
arg.FinalizedSweeplessSweepTx,
|
||||||
|
arg.SweepConfirmationHeight,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
DROP TABLE IF EXISTS instantout_updates;
|
||||||
|
DROP INDEX IF EXISTS instantout_swap_hash_idx;
|
||||||
|
DROP TABLE IF EXISTS instantout_swaps;
|
||||||
|
DROP INDEX IF EXISTS instantout_updates_swap_hash_idx;
|
||||||
|
`
|
@ -0,0 +1,52 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS instantout_swaps (
|
||||||
|
-- swap_hash points to the parent swap hash.
|
||||||
|
swap_hash BLOB PRIMARY KEY,
|
||||||
|
|
||||||
|
-- preimage is the preimage of the swap.
|
||||||
|
preimage BLOB NOT NULL,
|
||||||
|
|
||||||
|
-- sweep_address is the address that the server should sweep the funds to.
|
||||||
|
sweep_address TEXT NOT NULL,
|
||||||
|
|
||||||
|
-- OutgoingChanSet is the set of short ids of channels that may be used.
|
||||||
|
-- If empty, any channel may be used.
|
||||||
|
outgoing_chan_set TEXT NOT NULL,
|
||||||
|
|
||||||
|
-- htlc_fee_rate is the fee rate in sat/kw that is used for the htlc transaction.
|
||||||
|
htlc_fee_rate BIGINT NOT NULL,
|
||||||
|
|
||||||
|
-- reservation_ids is a list of ids of the reservations that are used for this swap.
|
||||||
|
reservation_ids BLOB NOT NULL,
|
||||||
|
|
||||||
|
-- swap_invoice is the invoice that is to be paid by the client to
|
||||||
|
-- initiate the loop out swap.
|
||||||
|
swap_invoice TEXT NOT NULL,
|
||||||
|
|
||||||
|
-- finalized_htlc_tx contains the fully signed htlc transaction.
|
||||||
|
finalized_htlc_tx BLOB,
|
||||||
|
|
||||||
|
-- sweep_tx_hash is the hash of the transaction that sweeps the htlc.
|
||||||
|
sweep_tx_hash BLOB,
|
||||||
|
|
||||||
|
-- finalized_sweepless_sweep_tx contains the fully signed sweepless sweep transaction.
|
||||||
|
finalized_sweepless_sweep_tx BLOB,
|
||||||
|
|
||||||
|
-- sweep_confirmation_height is the block height at which the sweep transaction is confirmed.
|
||||||
|
sweep_confirmation_height INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS instantout_updates (
|
||||||
|
-- id is auto incremented for each update.
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
|
||||||
|
-- swap_hash is the hash of the swap that this update is for.
|
||||||
|
swap_hash BLOB NOT NULL REFERENCES instantout_swaps(swap_hash),
|
||||||
|
|
||||||
|
-- update_state is the state of the swap at the time of the update.
|
||||||
|
update_state TEXT NOT NULL,
|
||||||
|
|
||||||
|
-- update_timestamp is the time at which the update was created.
|
||||||
|
update_timestamp TIMESTAMP NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS instantout_updates_swap_hash_idx ON instantout_updates(swap_hash);
|
@ -0,0 +1,75 @@
|
|||||||
|
-- name: InsertInstantOut :exec
|
||||||
|
INSERT INTO instantout_swaps (
|
||||||
|
swap_hash,
|
||||||
|
preimage,
|
||||||
|
sweep_address,
|
||||||
|
outgoing_chan_set,
|
||||||
|
htlc_fee_rate,
|
||||||
|
reservation_ids,
|
||||||
|
swap_invoice
|
||||||
|
) VALUES (
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3,
|
||||||
|
$4,
|
||||||
|
$5,
|
||||||
|
$6,
|
||||||
|
$7
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: UpdateInstantOut :exec
|
||||||
|
UPDATE instantout_swaps
|
||||||
|
SET
|
||||||
|
finalized_htlc_tx = $2,
|
||||||
|
sweep_tx_hash = $3,
|
||||||
|
finalized_sweepless_sweep_tx = $4,
|
||||||
|
sweep_confirmation_height = $5
|
||||||
|
WHERE
|
||||||
|
instantout_swaps.swap_hash = $1;
|
||||||
|
|
||||||
|
-- name: InsertInstantOutUpdate :exec
|
||||||
|
INSERT INTO instantout_updates (
|
||||||
|
swap_hash,
|
||||||
|
update_state,
|
||||||
|
update_timestamp
|
||||||
|
) VALUES (
|
||||||
|
$1,
|
||||||
|
$2,
|
||||||
|
$3
|
||||||
|
);
|
||||||
|
|
||||||
|
-- name: GetInstantOutSwap :one
|
||||||
|
SELECT
|
||||||
|
swaps.*,
|
||||||
|
instantout_swaps.*,
|
||||||
|
htlc_keys.*
|
||||||
|
FROM
|
||||||
|
swaps
|
||||||
|
JOIN
|
||||||
|
instantout_swaps ON swaps.swap_hash = instantout_swaps.swap_hash
|
||||||
|
JOIN
|
||||||
|
htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash
|
||||||
|
WHERE
|
||||||
|
swaps.swap_hash = $1;
|
||||||
|
|
||||||
|
-- name: GetInstantOutSwaps :many
|
||||||
|
SELECT
|
||||||
|
swaps.*,
|
||||||
|
instantout_swaps.*,
|
||||||
|
htlc_keys.*
|
||||||
|
FROM
|
||||||
|
swaps
|
||||||
|
JOIN
|
||||||
|
instantout_swaps ON swaps.swap_hash = instantout_swaps.swap_hash
|
||||||
|
JOIN
|
||||||
|
htlc_keys ON swaps.swap_hash = htlc_keys.swap_hash
|
||||||
|
ORDER BY
|
||||||
|
swaps.id;
|
||||||
|
|
||||||
|
-- name: GetInstantOutSwapUpdates :many
|
||||||
|
SELECT
|
||||||
|
instantout_updates.*
|
||||||
|
FROM
|
||||||
|
instantout_updates
|
||||||
|
WHERE
|
||||||
|
instantout_updates.swap_hash = $1;
|
Loading…
Reference in New Issue