mirror of https://github.com/lightninglabs/loop
instantout: add instantout store
parent
56ed6f7ccb
commit
ee0309f942
@ -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,4 @@
|
||||
DROP INDEX IF EXISTS instantout_updates_swap_hash_idx;
|
||||
DROP INDEX IF EXISTS instantout_swap_hash_idx;
|
||||
DROP TABLE IF EXISTS instantout_updates;
|
||||
DROP TABLE IF EXISTS instantout_swaps;
|
@ -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,
|
||||
|
||||
-- outgoing_chan_set 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