mirror of
https://github.com/lightninglabs/loop
synced 2024-11-09 19:10:47 +00:00
f40ef193e9
Previously we may have stored negative costs for some loop out swaps which this commit attempts to correct by fetching all completed swap, calculating the correct costs and overriding them in the database.
387 lines
9.1 KiB
Go
387 lines
9.1 KiB
Go
package loopdb
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/lightninglabs/loop/test"
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// StoreMock implements a mock client swap store.
|
|
type StoreMock struct {
|
|
LoopOutSwaps map[lntypes.Hash]*LoopOutContract
|
|
LoopOutUpdates map[lntypes.Hash][]SwapStateData
|
|
loopOutStoreChan chan LoopOutContract
|
|
loopOutUpdateChan chan SwapStateData
|
|
|
|
LoopInSwaps map[lntypes.Hash]*LoopInContract
|
|
LoopInUpdates map[lntypes.Hash][]SwapStateData
|
|
loopInStoreChan chan LoopInContract
|
|
loopInUpdateChan chan SwapStateData
|
|
|
|
migrations map[string]struct{}
|
|
|
|
t *testing.T
|
|
}
|
|
|
|
// NewStoreMock instantiates a new mock store.
|
|
func NewStoreMock(t *testing.T) *StoreMock {
|
|
return &StoreMock{
|
|
loopOutStoreChan: make(chan LoopOutContract, 1),
|
|
loopOutUpdateChan: make(chan SwapStateData, 1),
|
|
LoopOutSwaps: make(map[lntypes.Hash]*LoopOutContract),
|
|
LoopOutUpdates: make(map[lntypes.Hash][]SwapStateData),
|
|
|
|
loopInStoreChan: make(chan LoopInContract, 1),
|
|
loopInUpdateChan: make(chan SwapStateData, 1),
|
|
LoopInSwaps: make(map[lntypes.Hash]*LoopInContract),
|
|
LoopInUpdates: make(map[lntypes.Hash][]SwapStateData),
|
|
migrations: make(map[string]struct{}),
|
|
t: t,
|
|
}
|
|
}
|
|
|
|
// FetchLoopOutSwaps returns all swaps currently in the store.
|
|
//
|
|
// NOTE: Part of the SwapStore interface.
|
|
func (s *StoreMock) FetchLoopOutSwaps(ctx context.Context) ([]*LoopOut, error) {
|
|
result := []*LoopOut{}
|
|
|
|
for hash, contract := range s.LoopOutSwaps {
|
|
updates := s.LoopOutUpdates[hash]
|
|
events := make([]*LoopEvent, len(updates))
|
|
for i, u := range updates {
|
|
events[i] = &LoopEvent{
|
|
SwapStateData: u,
|
|
}
|
|
}
|
|
|
|
swap := &LoopOut{
|
|
Loop: Loop{
|
|
Hash: hash,
|
|
Events: events,
|
|
},
|
|
Contract: contract,
|
|
}
|
|
result = append(result, swap)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// FetchLoopOutSwaps returns all swaps currently in the store.
|
|
//
|
|
// NOTE: Part of the SwapStore interface.
|
|
func (s *StoreMock) FetchLoopOutSwap(ctx context.Context,
|
|
hash lntypes.Hash) (*LoopOut, error) {
|
|
|
|
contract, ok := s.LoopOutSwaps[hash]
|
|
if !ok {
|
|
return nil, errors.New("swap not found")
|
|
}
|
|
|
|
updates := s.LoopOutUpdates[hash]
|
|
events := make([]*LoopEvent, len(updates))
|
|
for i, u := range updates {
|
|
events[i] = &LoopEvent{
|
|
SwapStateData: u,
|
|
}
|
|
}
|
|
|
|
swap := &LoopOut{
|
|
Loop: Loop{
|
|
Hash: hash,
|
|
Events: events,
|
|
},
|
|
Contract: contract,
|
|
}
|
|
|
|
return swap, nil
|
|
}
|
|
|
|
// CreateLoopOut adds an initiated swap to the store.
|
|
//
|
|
// NOTE: Part of the SwapStore interface.
|
|
func (s *StoreMock) CreateLoopOut(ctx context.Context, hash lntypes.Hash,
|
|
swap *LoopOutContract) error {
|
|
|
|
_, ok := s.LoopOutSwaps[hash]
|
|
if ok {
|
|
return errors.New("swap already exists")
|
|
}
|
|
|
|
s.LoopOutSwaps[hash] = swap
|
|
s.LoopOutUpdates[hash] = []SwapStateData{}
|
|
s.loopOutStoreChan <- *swap
|
|
|
|
return nil
|
|
}
|
|
|
|
// FetchLoopInSwaps returns all in swaps currently in the store.
|
|
func (s *StoreMock) FetchLoopInSwaps(ctx context.Context) ([]*LoopIn,
|
|
error) {
|
|
|
|
result := []*LoopIn{}
|
|
|
|
for hash, contract := range s.LoopInSwaps {
|
|
updates := s.LoopInUpdates[hash]
|
|
events := make([]*LoopEvent, len(updates))
|
|
for i, u := range updates {
|
|
events[i] = &LoopEvent{
|
|
SwapStateData: u,
|
|
}
|
|
}
|
|
|
|
swap := &LoopIn{
|
|
Loop: Loop{
|
|
Hash: hash,
|
|
Events: events,
|
|
},
|
|
Contract: contract,
|
|
}
|
|
result = append(result, swap)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// CreateLoopIn adds an initiated loop in swap to the store.
|
|
//
|
|
// NOTE: Part of the SwapStore interface.
|
|
func (s *StoreMock) CreateLoopIn(ctx context.Context, hash lntypes.Hash,
|
|
swap *LoopInContract) error {
|
|
|
|
_, ok := s.LoopInSwaps[hash]
|
|
if ok {
|
|
return errors.New("swap already exists")
|
|
}
|
|
|
|
s.LoopInSwaps[hash] = swap
|
|
s.LoopInUpdates[hash] = []SwapStateData{}
|
|
s.loopInStoreChan <- *swap
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateLoopOut stores a new event for a target loop out swap. This appends to
|
|
// the event log for a particular swap as it goes through the various stages in
|
|
// its lifetime.
|
|
//
|
|
// NOTE: Part of the SwapStore interface.
|
|
func (s *StoreMock) UpdateLoopOut(ctx context.Context, hash lntypes.Hash,
|
|
time time.Time, state SwapStateData) error {
|
|
|
|
updates, ok := s.LoopOutUpdates[hash]
|
|
if !ok {
|
|
return errors.New("swap does not exists")
|
|
}
|
|
|
|
updates = append(updates, state)
|
|
s.LoopOutUpdates[hash] = updates
|
|
s.loopOutUpdateChan <- state
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateLoopIn stores a new event for a target loop in swap. This appends to
|
|
// the event log for a particular swap as it goes through the various stages in
|
|
// its lifetime.
|
|
//
|
|
// NOTE: Part of the SwapStore interface.
|
|
func (s *StoreMock) UpdateLoopIn(ctx context.Context, hash lntypes.Hash,
|
|
time time.Time, state SwapStateData) error {
|
|
|
|
updates, ok := s.LoopInUpdates[hash]
|
|
if !ok {
|
|
return errors.New("swap does not exists")
|
|
}
|
|
|
|
updates = append(updates, state)
|
|
s.LoopInUpdates[hash] = updates
|
|
s.loopInUpdateChan <- state
|
|
|
|
return nil
|
|
}
|
|
|
|
// PutLiquidityParams writes the serialized `manager.Parameters` bytes into the
|
|
// bucket.
|
|
//
|
|
// NOTE: Part of the SwapStore interface.
|
|
func (s *StoreMock) PutLiquidityParams(ctx context.Context,
|
|
params []byte) error {
|
|
|
|
return nil
|
|
}
|
|
|
|
// FetchLiquidityParams reads the serialized `manager.Parameters` bytes from
|
|
// the bucket.
|
|
//
|
|
// NOTE: Part of the SwapStore interface.
|
|
func (s *StoreMock) FetchLiquidityParams(ctx context.Context) ([]byte, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
// Close closes the store.
|
|
func (s *StoreMock) Close() error {
|
|
return nil
|
|
}
|
|
|
|
// isDone asserts that the store mock has no pending operations.
|
|
func (s *StoreMock) IsDone() error {
|
|
select {
|
|
case <-s.loopOutStoreChan:
|
|
return errors.New("storeChan not empty")
|
|
default:
|
|
}
|
|
|
|
select {
|
|
case <-s.loopOutUpdateChan:
|
|
return errors.New("updateChan not empty")
|
|
default:
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AssertLoopOutStored asserts that a swap is stored.
|
|
func (s *StoreMock) AssertLoopOutStored() {
|
|
s.t.Helper()
|
|
|
|
select {
|
|
case <-s.loopOutStoreChan:
|
|
case <-time.After(test.Timeout):
|
|
s.t.Fatalf("expected swap to be stored")
|
|
}
|
|
}
|
|
|
|
// AssertLoopOutState asserts that a specified state transition is persisted to
|
|
// disk.
|
|
func (s *StoreMock) AssertLoopOutState(expectedState SwapState) {
|
|
s.t.Helper()
|
|
|
|
select {
|
|
case state := <-s.loopOutUpdateChan:
|
|
require.Equal(s.t, expectedState, state.State)
|
|
case <-time.After(test.Timeout):
|
|
s.t.Fatalf("expected swap state to be stored")
|
|
}
|
|
}
|
|
|
|
// AssertLoopInStored asserts that a loop-in swap is stored.
|
|
func (s *StoreMock) AssertLoopInStored() {
|
|
s.t.Helper()
|
|
|
|
select {
|
|
case <-s.loopInStoreChan:
|
|
case <-time.After(test.Timeout):
|
|
s.t.Fatalf("expected swap to be stored")
|
|
}
|
|
}
|
|
|
|
// assertLoopInState asserts that a specified state transition is persisted to
|
|
// disk.
|
|
func (s *StoreMock) AssertLoopInState(
|
|
expectedState SwapState) SwapStateData {
|
|
|
|
s.t.Helper()
|
|
|
|
state := <-s.loopInUpdateChan
|
|
require.Equal(s.t, expectedState, state.State)
|
|
|
|
return state
|
|
}
|
|
|
|
// AssertStorePreimageReveal asserts that a swap is marked as preimage revealed.
|
|
func (s *StoreMock) AssertStorePreimageReveal() {
|
|
s.t.Helper()
|
|
|
|
select {
|
|
case state := <-s.loopOutUpdateChan:
|
|
require.Equal(s.t, StatePreimageRevealed, state.State)
|
|
|
|
case <-time.After(test.Timeout):
|
|
s.t.Fatalf("expected swap to be marked as preimage revealed")
|
|
}
|
|
}
|
|
|
|
// AssertStoreFinished asserts that a swap is marked as finished.
|
|
func (s *StoreMock) AssertStoreFinished(expectedResult SwapState) {
|
|
s.t.Helper()
|
|
|
|
select {
|
|
case state := <-s.loopOutUpdateChan:
|
|
require.Equal(s.t, expectedResult, state.State)
|
|
|
|
case <-time.After(test.Timeout):
|
|
s.t.Fatalf("expected swap to be finished")
|
|
}
|
|
}
|
|
|
|
// BatchCreateLoopOut creates many loop out swaps in a batch.
|
|
func (b *StoreMock) BatchCreateLoopOut(ctx context.Context,
|
|
swaps map[lntypes.Hash]*LoopOutContract) error {
|
|
|
|
return errors.New("not implemented")
|
|
}
|
|
|
|
// BatchCreateLoopIn creates many loop in swaps in a batch.
|
|
func (b *StoreMock) BatchCreateLoopIn(ctx context.Context,
|
|
swaps map[lntypes.Hash]*LoopInContract) error {
|
|
|
|
return errors.New("not implemented")
|
|
}
|
|
|
|
// BatchInsertUpdate inserts many updates for a swap in a batch.
|
|
func (b *StoreMock) BatchInsertUpdate(ctx context.Context,
|
|
updateData map[lntypes.Hash][]BatchInsertUpdateData) error {
|
|
|
|
return errors.New("not implemented")
|
|
}
|
|
|
|
// BatchUpdateLoopOutSwapCosts updates the swap costs for a batch of loop out
|
|
// swaps.
|
|
func (s *StoreMock) BatchUpdateLoopOutSwapCosts(ctx context.Context,
|
|
costs map[lntypes.Hash]SwapCost) error {
|
|
|
|
for hash, cost := range costs {
|
|
if _, ok := s.LoopOutUpdates[hash]; !ok {
|
|
return fmt.Errorf("swap has no updates: %v", hash)
|
|
}
|
|
|
|
updates, ok := s.LoopOutUpdates[hash]
|
|
if !ok {
|
|
return fmt.Errorf("swap has no updates: %v", hash)
|
|
}
|
|
|
|
updates[len(updates)-1].Cost = cost
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// HasMigration returns true if the migration with the given ID has been done.
|
|
func (s *StoreMock) HasMigration(ctx context.Context, migrationID string) (
|
|
bool, error) {
|
|
|
|
_, ok := s.migrations[migrationID]
|
|
|
|
return ok, nil
|
|
}
|
|
|
|
// SetMigration marks the migration with the given ID as done.
|
|
func (s *StoreMock) SetMigration(ctx context.Context,
|
|
migrationID string) error {
|
|
|
|
if _, ok := s.migrations[migrationID]; ok {
|
|
return errors.New("migration already done")
|
|
}
|
|
|
|
s.migrations[migrationID] = struct{}{}
|
|
|
|
return nil
|
|
}
|