2023-09-05 13:48:25 -04:00
|
|
|
package sweepbatcher
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
2025-11-12 02:33:43 -03:00
|
|
|
"fmt"
|
2023-09-05 13:48:25 -04:00
|
|
|
"sort"
|
2025-02-25 00:20:59 -03:00
|
|
|
"sync"
|
2023-09-05 13:48:25 -04:00
|
|
|
|
2024-02-02 22:07:40 +01:00
|
|
|
"github.com/btcsuite/btcd/btcutil"
|
2025-03-25 14:30:52 -03:00
|
|
|
"github.com/btcsuite/btcd/wire"
|
2023-09-05 13:48:25 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// StoreMock implements a mock client swap store.
|
|
|
|
|
type StoreMock struct {
|
2024-05-30 13:18:41 -03:00
|
|
|
batches map[int32]dbBatch
|
2025-03-25 14:30:52 -03:00
|
|
|
sweeps map[wire.OutPoint]dbSweep
|
2025-02-25 00:20:59 -03:00
|
|
|
mu sync.Mutex
|
2025-04-21 00:57:17 -03:00
|
|
|
sweepID int32
|
2025-06-17 14:01:59 -03:00
|
|
|
batchID int32
|
2023-09-05 13:48:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewStoreMock instantiates a new mock store.
|
2024-05-30 13:18:41 -03:00
|
|
|
func NewStoreMock() *StoreMock {
|
2023-09-05 13:48:25 -04:00
|
|
|
return &StoreMock{
|
2024-05-30 13:18:41 -03:00
|
|
|
batches: make(map[int32]dbBatch),
|
2025-03-25 14:30:52 -03:00
|
|
|
sweeps: make(map[wire.OutPoint]dbSweep),
|
2023-09-05 13:48:25 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-14 11:41:34 -03:00
|
|
|
// FetchUnconfirmedSweepBatches fetches all the loop out sweep batches from the
|
2023-09-05 13:48:25 -04:00
|
|
|
// database that are not in a confirmed state.
|
|
|
|
|
func (s *StoreMock) FetchUnconfirmedSweepBatches(ctx context.Context) (
|
|
|
|
|
[]*dbBatch, error) {
|
|
|
|
|
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2023-09-05 13:48:25 -04:00
|
|
|
result := []*dbBatch{}
|
|
|
|
|
for _, batch := range s.batches {
|
2025-04-08 20:44:30 -03:00
|
|
|
if !batch.Confirmed {
|
2023-09-05 13:48:25 -04:00
|
|
|
result = append(result, &batch)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// InsertSweepBatch inserts a batch into the database, returning the id of the
|
|
|
|
|
// inserted batch.
|
|
|
|
|
func (s *StoreMock) InsertSweepBatch(ctx context.Context,
|
|
|
|
|
batch *dbBatch) (int32, error) {
|
|
|
|
|
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2025-06-17 14:01:59 -03:00
|
|
|
id := s.batchID
|
|
|
|
|
s.batchID++
|
2023-09-05 13:48:25 -04:00
|
|
|
|
|
|
|
|
s.batches[id] = *batch
|
|
|
|
|
return id, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-17 14:01:59 -03:00
|
|
|
// CancelBatch drops a batch from the database.
|
|
|
|
|
func (s *StoreMock) CancelBatch(ctx context.Context, id int32) error {
|
2024-05-24 16:20:50 +02:00
|
|
|
delete(s.batches, id)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-05 13:48:25 -04:00
|
|
|
// UpdateSweepBatch updates a batch in the database.
|
|
|
|
|
func (s *StoreMock) UpdateSweepBatch(ctx context.Context,
|
|
|
|
|
batch *dbBatch) error {
|
|
|
|
|
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2023-09-05 13:48:25 -04:00
|
|
|
s.batches[batch.ID] = *batch
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-12 02:33:43 -03:00
|
|
|
// ConfirmBatchWithSweeps updates the batch and the provided sweeps atomically.
|
|
|
|
|
func (s *StoreMock) ConfirmBatchWithSweeps(ctx context.Context,
|
|
|
|
|
batch *dbBatch, sweeps []*dbSweep) error {
|
|
|
|
|
|
|
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
s.batches[batch.ID] = *batch
|
|
|
|
|
|
|
|
|
|
for _, sweep := range sweeps {
|
|
|
|
|
sweepCopy := *sweep
|
|
|
|
|
|
|
|
|
|
old, exists := s.sweeps[sweep.Outpoint]
|
|
|
|
|
if !exists {
|
|
|
|
|
return fmt.Errorf("confirming unknown sweep %v",
|
|
|
|
|
sweep.Outpoint)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sweepCopy.ID = old.ID
|
|
|
|
|
|
|
|
|
|
s.sweeps[sweep.Outpoint] = sweepCopy
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-05 13:48:25 -04:00
|
|
|
// FetchBatchSweeps fetches all the sweeps that belong to a batch.
|
|
|
|
|
func (s *StoreMock) FetchBatchSweeps(ctx context.Context,
|
|
|
|
|
id int32) ([]*dbSweep, error) {
|
|
|
|
|
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2023-09-05 13:48:25 -04:00
|
|
|
result := []*dbSweep{}
|
|
|
|
|
for _, sweep := range s.sweeps {
|
2024-05-30 13:18:41 -03:00
|
|
|
if sweep.BatchID == id {
|
|
|
|
|
result = append(result, &sweep)
|
2023-09-05 13:48:25 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sort.Slice(result, func(i, j int) bool {
|
|
|
|
|
return result[i].ID < result[j].ID
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UpsertSweep inserts a sweep into the database, or updates an existing sweep.
|
|
|
|
|
func (s *StoreMock) UpsertSweep(ctx context.Context, sweep *dbSweep) error {
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2025-04-21 00:57:17 -03:00
|
|
|
sweepCopy := *sweep
|
|
|
|
|
|
|
|
|
|
if old, exists := s.sweeps[sweep.Outpoint]; exists {
|
|
|
|
|
// Preserve existing sweep ID.
|
|
|
|
|
sweepCopy.ID = old.ID
|
|
|
|
|
} else {
|
|
|
|
|
// Assign fresh sweep ID.
|
|
|
|
|
sweepCopy.ID = s.sweepID
|
|
|
|
|
s.sweepID++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.sweeps[sweep.Outpoint] = sweepCopy
|
2025-02-25 00:20:59 -03:00
|
|
|
|
2023-09-05 13:48:25 -04:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetSweepStatus returns the status of a sweep.
|
|
|
|
|
func (s *StoreMock) GetSweepStatus(ctx context.Context,
|
2025-03-25 14:30:52 -03:00
|
|
|
outpoint wire.OutPoint) (bool, error) {
|
2023-09-05 13:48:25 -04:00
|
|
|
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2025-03-25 14:30:52 -03:00
|
|
|
sweep, ok := s.sweeps[outpoint]
|
2023-09-05 13:48:25 -04:00
|
|
|
if !ok {
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sweep.Completed, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close closes the store.
|
|
|
|
|
func (s *StoreMock) Close() error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AssertSweepStored asserts that a sweep is stored.
|
2025-03-25 14:30:52 -03:00
|
|
|
func (s *StoreMock) AssertSweepStored(outpoint wire.OutPoint) bool {
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2025-03-25 14:30:52 -03:00
|
|
|
_, ok := s.sweeps[outpoint]
|
2023-09-05 13:48:25 -04:00
|
|
|
return ok
|
|
|
|
|
}
|
2024-02-02 22:07:40 +01:00
|
|
|
|
|
|
|
|
// GetParentBatch returns the parent batch of a swap.
|
2025-03-25 14:30:52 -03:00
|
|
|
func (s *StoreMock) GetParentBatch(ctx context.Context,
|
|
|
|
|
outpoint wire.OutPoint) (*dbBatch, error) {
|
2024-02-02 22:07:40 +01:00
|
|
|
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2024-02-02 22:07:40 +01:00
|
|
|
for _, sweep := range s.sweeps {
|
2025-03-25 14:30:52 -03:00
|
|
|
if sweep.Outpoint == outpoint {
|
2024-02-02 22:07:40 +01:00
|
|
|
batch, ok := s.batches[sweep.BatchID]
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, errors.New("batch not found")
|
|
|
|
|
}
|
|
|
|
|
return &batch, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil, errors.New("batch not found")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TotalSweptAmount returns the total amount of BTC that has been swept from a
|
|
|
|
|
// batch.
|
|
|
|
|
func (s *StoreMock) TotalSweptAmount(ctx context.Context, batchID int32) (
|
|
|
|
|
btcutil.Amount, error) {
|
|
|
|
|
|
2025-02-25 00:20:59 -03:00
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
2024-02-02 22:07:40 +01:00
|
|
|
batch, ok := s.batches[batchID]
|
|
|
|
|
if !ok {
|
|
|
|
|
return 0, errors.New("batch not found")
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 20:44:30 -03:00
|
|
|
if !batch.Confirmed {
|
2024-02-02 22:07:40 +01:00
|
|
|
return 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var total btcutil.Amount
|
|
|
|
|
for _, sweep := range s.sweeps {
|
|
|
|
|
if sweep.BatchID == batchID {
|
|
|
|
|
total += sweep.Amount
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 18:07:13 -03:00
|
|
|
return total, nil
|
2024-02-02 22:07:40 +01:00
|
|
|
}
|