2019-03-07 02:19:57 +00:00
|
|
|
package loopdb
|
2019-03-06 20:13:50 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
|
|
|
"io/ioutil"
|
2019-03-07 02:19:57 +00:00
|
|
|
"os"
|
2019-03-06 20:13:50 +00:00
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2019-03-25 10:06:16 +00:00
|
|
|
"github.com/btcsuite/btcd/chaincfg"
|
2019-03-06 23:29:44 +00:00
|
|
|
"github.com/lightninglabs/loop/test"
|
2019-03-07 02:19:57 +00:00
|
|
|
"github.com/lightningnetwork/lnd/lntypes"
|
2019-03-06 20:13:50 +00:00
|
|
|
)
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
var (
|
|
|
|
senderKey = [33]byte{
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
|
|
|
|
}
|
|
|
|
|
|
|
|
receiverKey = [33]byte{
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
|
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
|
|
|
|
}
|
|
|
|
|
|
|
|
testPreimage = lntypes.Preimage([32]byte{
|
|
|
|
1, 1, 1, 1, 2, 2, 2, 2,
|
|
|
|
3, 3, 3, 3, 4, 4, 4, 4,
|
|
|
|
1, 1, 1, 1, 2, 2, 2, 2,
|
|
|
|
3, 3, 3, 3, 4, 4, 4, 4,
|
|
|
|
})
|
|
|
|
|
|
|
|
testTime = time.Date(2018, time.January, 9, 14, 00, 00, 0, time.UTC)
|
|
|
|
)
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-12 15:09:57 +00:00
|
|
|
// TestLoopOutStore tests all the basic functionality of the current bbolt
|
2019-03-07 02:19:57 +00:00
|
|
|
// swap store.
|
2019-03-12 15:09:57 +00:00
|
|
|
func TestLoopOutStore(t *testing.T) {
|
2019-03-06 20:13:50 +00:00
|
|
|
tempDirName, err := ioutil.TempDir("", "clientstore")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2019-03-07 02:19:57 +00:00
|
|
|
defer os.RemoveAll(tempDirName)
|
2019-03-06 20:13:50 +00:00
|
|
|
|
2019-03-25 10:06:16 +00:00
|
|
|
store, err := NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// First, verify that an empty database has no active swaps.
|
2019-03-07 04:32:24 +00:00
|
|
|
swaps, err := store.FetchLoopOutSwaps()
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(swaps) != 0 {
|
|
|
|
t.Fatal("expected empty store")
|
|
|
|
}
|
|
|
|
|
|
|
|
destAddr := test.GetDestAddr(t, 0)
|
|
|
|
hash := sha256.Sum256(testPreimage[:])
|
|
|
|
initiationTime := time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// Next, we'll make a new pending swap that we'll insert into the
|
|
|
|
// database shortly.
|
2019-03-07 04:32:24 +00:00
|
|
|
pendingSwap := LoopOutContract{
|
2019-03-06 20:13:50 +00:00
|
|
|
SwapContract: SwapContract{
|
2019-03-12 15:09:57 +00:00
|
|
|
AmountRequested: 100,
|
|
|
|
Preimage: testPreimage,
|
|
|
|
CltvExpiry: 144,
|
|
|
|
SenderKey: senderKey,
|
|
|
|
|
|
|
|
ReceiverKey: receiverKey,
|
|
|
|
MaxMinerFee: 10,
|
|
|
|
MaxSwapFee: 20,
|
|
|
|
|
|
|
|
InitiationHeight: 99,
|
2019-03-06 20:13:50 +00:00
|
|
|
|
|
|
|
// Convert to/from unix to remove timezone, so that it
|
|
|
|
// doesn't interfere with DeepEqual.
|
|
|
|
InitiationTime: time.Unix(0, initiationTime.UnixNano()),
|
|
|
|
},
|
2019-03-12 15:09:57 +00:00
|
|
|
MaxPrepayRoutingFee: 40,
|
|
|
|
PrepayInvoice: "prepayinvoice",
|
|
|
|
DestAddr: destAddr,
|
|
|
|
SwapInvoice: "swapinvoice",
|
|
|
|
MaxSwapRoutingFee: 30,
|
|
|
|
SweepConfTarget: 2,
|
2019-03-06 20:13:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// checkSwap is a test helper function that'll assert the state of a
|
|
|
|
// swap.
|
2019-03-06 20:13:50 +00:00
|
|
|
checkSwap := func(expectedState SwapState) {
|
|
|
|
t.Helper()
|
|
|
|
|
2019-03-07 04:32:24 +00:00
|
|
|
swaps, err := store.FetchLoopOutSwaps()
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(swaps) != 1 {
|
|
|
|
t.Fatal("expected pending swap in store")
|
|
|
|
}
|
|
|
|
|
|
|
|
swap := swaps[0].Contract
|
|
|
|
if !reflect.DeepEqual(swap, &pendingSwap) {
|
|
|
|
t.Fatal("invalid pending swap data")
|
|
|
|
}
|
|
|
|
|
|
|
|
if swaps[0].State() != expectedState {
|
|
|
|
t.Fatalf("expected state %v, but got %v",
|
|
|
|
expectedState, swaps[0].State(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// If we create a new swap, then it should show up as being initialized
|
|
|
|
// right after.
|
2019-03-07 04:32:24 +00:00
|
|
|
if err := store.CreateLoopOut(hash, &pendingSwap); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
checkSwap(StateInitiated)
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// Trying to make the same swap again should result in an error.
|
2019-03-07 04:32:24 +00:00
|
|
|
if err := store.CreateLoopOut(hash, &pendingSwap); err == nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
t.Fatal("expected error on storing duplicate")
|
|
|
|
}
|
|
|
|
checkSwap(StateInitiated)
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// Next, we'll update to the next state of the pre-image being
|
|
|
|
// revealed. The state should be reflected here again.
|
2019-03-07 04:32:24 +00:00
|
|
|
err = store.UpdateLoopOut(
|
2019-03-07 02:19:57 +00:00
|
|
|
hash, testTime, StatePreimageRevealed,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
checkSwap(StatePreimageRevealed)
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// Next, we'll update to the final state to ensure that the state is
|
|
|
|
// properly updated.
|
2019-03-07 04:32:24 +00:00
|
|
|
err = store.UpdateLoopOut(
|
2019-03-07 02:19:57 +00:00
|
|
|
hash, testTime, StateFailInsufficientValue,
|
|
|
|
)
|
|
|
|
if err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
checkSwap(StateFailInsufficientValue)
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
if err := store.Close(); err != nil {
|
2019-03-06 20:13:50 +00:00
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2019-03-07 02:19:57 +00:00
|
|
|
// If we re-open the same store, then the state of the current swap
|
|
|
|
// should be the same.
|
2019-03-25 10:06:16 +00:00
|
|
|
store, err = NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
|
2019-03-06 20:13:50 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
checkSwap(StateFailInsufficientValue)
|
|
|
|
}
|
2019-03-12 15:09:57 +00:00
|
|
|
|
|
|
|
// TestLoopInStore tests all the basic functionality of the current bbolt
|
|
|
|
// swap store.
|
|
|
|
func TestLoopInStore(t *testing.T) {
|
|
|
|
tempDirName, err := ioutil.TempDir("", "clientstore")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(tempDirName)
|
|
|
|
|
|
|
|
store, err := NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// First, verify that an empty database has no active swaps.
|
|
|
|
swaps, err := store.FetchLoopInSwaps()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(swaps) != 0 {
|
|
|
|
t.Fatal("expected empty store")
|
|
|
|
}
|
|
|
|
|
|
|
|
hash := sha256.Sum256(testPreimage[:])
|
|
|
|
initiationTime := time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
|
|
|
|
// Next, we'll make a new pending swap that we'll insert into the
|
|
|
|
// database shortly.
|
|
|
|
loopInChannel := uint64(123)
|
|
|
|
|
|
|
|
pendingSwap := LoopInContract{
|
|
|
|
SwapContract: SwapContract{
|
|
|
|
AmountRequested: 100,
|
|
|
|
Preimage: testPreimage,
|
|
|
|
CltvExpiry: 144,
|
|
|
|
SenderKey: senderKey,
|
|
|
|
ReceiverKey: receiverKey,
|
|
|
|
MaxMinerFee: 10,
|
|
|
|
MaxSwapFee: 20,
|
|
|
|
InitiationHeight: 99,
|
|
|
|
|
|
|
|
// Convert to/from unix to remove timezone, so that it
|
|
|
|
// doesn't interfere with DeepEqual.
|
|
|
|
InitiationTime: time.Unix(0, initiationTime.UnixNano()),
|
|
|
|
},
|
|
|
|
HtlcConfTarget: 2,
|
|
|
|
LoopInChannel: &loopInChannel,
|
2019-03-28 12:29:21 +00:00
|
|
|
ExternalHtlc: true,
|
2019-03-12 15:09:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// checkSwap is a test helper function that'll assert the state of a
|
|
|
|
// swap.
|
|
|
|
checkSwap := func(expectedState SwapState) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
swaps, err := store.FetchLoopInSwaps()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(swaps) != 1 {
|
|
|
|
t.Fatal("expected pending swap in store")
|
|
|
|
}
|
|
|
|
|
|
|
|
swap := swaps[0].Contract
|
|
|
|
if !reflect.DeepEqual(swap, &pendingSwap) {
|
|
|
|
t.Fatal("invalid pending swap data")
|
|
|
|
}
|
|
|
|
|
|
|
|
if swaps[0].State() != expectedState {
|
|
|
|
t.Fatalf("expected state %v, but got %v",
|
|
|
|
expectedState, swaps[0].State(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we create a new swap, then it should show up as being initialized
|
|
|
|
// right after.
|
|
|
|
if err := store.CreateLoopIn(hash, &pendingSwap); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
checkSwap(StateInitiated)
|
|
|
|
|
|
|
|
// Trying to make the same swap again should result in an error.
|
|
|
|
if err := store.CreateLoopIn(hash, &pendingSwap); err == nil {
|
|
|
|
t.Fatal("expected error on storing duplicate")
|
|
|
|
}
|
|
|
|
checkSwap(StateInitiated)
|
|
|
|
|
|
|
|
// Next, we'll update to the next state of the pre-image being
|
|
|
|
// revealed. The state should be reflected here again.
|
|
|
|
err = store.UpdateLoopIn(
|
|
|
|
hash, testTime, StatePreimageRevealed,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
checkSwap(StatePreimageRevealed)
|
|
|
|
|
|
|
|
// Next, we'll update to the final state to ensure that the state is
|
|
|
|
// properly updated.
|
|
|
|
err = store.UpdateLoopIn(
|
|
|
|
hash, testTime, StateFailInsufficientValue,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
checkSwap(StateFailInsufficientValue)
|
|
|
|
|
|
|
|
if err := store.Close(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we re-open the same store, then the state of the current swap
|
|
|
|
// should be the same.
|
|
|
|
store, err = NewBoltSwapStore(tempDirName, &chaincfg.MainNetParams)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
checkSwap(StateFailInsufficientValue)
|
|
|
|
}
|