decred.org/dcrdex@v1.0.5/client/asset/btc/txdb_test.go (about)

     1  // This code is available on the terms of the project LICENSE.md file,
     2  // also available online at https://blueoakcouncil.org/license/1.0.0.
     3  
     4  package btc
     5  
     6  import (
     7  	"context"
     8  	"encoding/hex"
     9  	"errors"
    10  	"reflect"
    11  	"testing"
    12  
    13  	"decred.org/dcrdex/client/asset"
    14  	"decred.org/dcrdex/dex"
    15  	"decred.org/dcrdex/dex/encode"
    16  )
    17  
    18  func TestTxDB(t *testing.T) {
    19  	tempDir := t.TempDir()
    20  	tLogger := dex.StdOutLogger("TXDB", dex.LevelTrace)
    21  
    22  	txHistoryStore := NewBadgerTxDB(tempDir, tLogger)
    23  
    24  	ctx, cancel := context.WithCancel(context.Background())
    25  	defer cancel()
    26  
    27  	wg, err := txHistoryStore.Connect(ctx)
    28  	if err != nil {
    29  		t.Fatalf("error connecting to tx history store: %v", err)
    30  	}
    31  	defer func() {
    32  		cancel()
    33  		wg.Wait()
    34  	}()
    35  
    36  	txs, err := txHistoryStore.GetTxs(0, nil, true)
    37  	if err != nil {
    38  		t.Fatalf("error retrieving txs: %v", err)
    39  	}
    40  	if len(txs) != 0 {
    41  		t.Fatalf("expected 0 txs but got %d", len(txs))
    42  	}
    43  
    44  	tx1 := &ExtendedWalletTx{
    45  		WalletTransaction: &asset.WalletTransaction{
    46  			Type:        asset.Send,
    47  			ID:          hex.EncodeToString(encode.RandomBytes(32)),
    48  			Amount:      1e8,
    49  			Fees:        1e5,
    50  			BlockNumber: 0,
    51  		},
    52  		Submitted: false,
    53  	}
    54  
    55  	tx2 := &ExtendedWalletTx{
    56  		WalletTransaction: &asset.WalletTransaction{
    57  			Type:        asset.Receive,
    58  			ID:          hex.EncodeToString(encode.RandomBytes(32)),
    59  			Amount:      1e8,
    60  			Fees:        3e5,
    61  			BlockNumber: 0,
    62  		},
    63  		Submitted: true,
    64  	}
    65  
    66  	tx3 := &ExtendedWalletTx{
    67  		WalletTransaction: &asset.WalletTransaction{
    68  			Type:        asset.Swap,
    69  			ID:          hex.EncodeToString(encode.RandomBytes(32)),
    70  			Amount:      1e8,
    71  			Fees:        2e5,
    72  			BlockNumber: 0,
    73  		},
    74  		Submitted: true,
    75  	}
    76  
    77  	GetTxsAndCheck := func(n int, refID *string, past bool, expected []*asset.WalletTransaction) {
    78  		t.Helper()
    79  
    80  		txs, err = txHistoryStore.GetTxs(n, refID, past)
    81  		if err != nil {
    82  			t.Fatalf("failed to get txs: %v", err)
    83  		}
    84  		if len(txs) != len(expected) {
    85  			t.Fatalf("expected %d txs but got %d", len(expected), len(txs))
    86  		}
    87  		for i, expectedTx := range expected {
    88  			if !reflect.DeepEqual(expectedTx, txs[i]) {
    89  				t.Fatalf("transaction %d: %+v != %+v", i, expectedTx, txs[i])
    90  			}
    91  		}
    92  	}
    93  
    94  	getPendingTxsAndCheck := func(expected []*ExtendedWalletTx) {
    95  		t.Helper()
    96  
    97  		txs, err := txHistoryStore.GetPendingTxs()
    98  		if err != nil {
    99  			t.Fatalf("failed to get unconfirmed txs: %v", err)
   100  		}
   101  
   102  		if len(txs) != len(expected) {
   103  			t.Fatalf("expected %d txs but got %d", len(expected), len(txs))
   104  		}
   105  
   106  		for i, expectedTx := range expected {
   107  			if !reflect.DeepEqual(expectedTx.WalletTransaction, txs[i].WalletTransaction) {
   108  				t.Fatalf("transaction %+v != %+v", expectedTx.WalletTransaction, txs[i].WalletTransaction)
   109  			}
   110  		}
   111  	}
   112  
   113  	err = txHistoryStore.StoreTx(tx1)
   114  	if err != nil {
   115  		t.Fatalf("failed to store tx: %v", err)
   116  	}
   117  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{})
   118  	getPendingTxsAndCheck([]*ExtendedWalletTx{tx1})
   119  
   120  	err = txHistoryStore.MarkTxAsSubmitted(tx1.ID)
   121  	if err != nil {
   122  		t.Fatalf("failed to mark tx as submitted: %v", err)
   123  	}
   124  	tx1.Submitted = true
   125  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{tx1.WalletTransaction})
   126  	getPendingTxsAndCheck([]*ExtendedWalletTx{tx1})
   127  
   128  	// Storing same pending tx twice should not change anything.
   129  	err = txHistoryStore.StoreTx(tx1)
   130  	if err != nil {
   131  		t.Fatalf("failed to store tx: %v", err)
   132  	}
   133  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{tx1.WalletTransaction})
   134  	getPendingTxsAndCheck([]*ExtendedWalletTx{tx1})
   135  
   136  	tx1.BlockNumber = 100
   137  	err = txHistoryStore.StoreTx(tx1)
   138  	if err != nil {
   139  		t.Fatalf("failed to store tx: %v", err)
   140  	}
   141  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{tx1.WalletTransaction})
   142  	getPendingTxsAndCheck([]*ExtendedWalletTx{tx1})
   143  
   144  	err = txHistoryStore.StoreTx(tx2)
   145  	if err != nil {
   146  		t.Fatalf("failed to store tx: %v", err)
   147  	}
   148  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{tx2.WalletTransaction, tx1.WalletTransaction})
   149  	getPendingTxsAndCheck([]*ExtendedWalletTx{tx2, tx1})
   150  
   151  	tx2.BlockNumber = 99
   152  	tx2.Confirmed = true
   153  	err = txHistoryStore.StoreTx(tx2)
   154  	if err != nil {
   155  		t.Fatalf("failed to store tx: %v", err)
   156  	}
   157  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{tx1.WalletTransaction, tx2.WalletTransaction})
   158  	getPendingTxsAndCheck([]*ExtendedWalletTx{tx1})
   159  
   160  	err = txHistoryStore.StoreTx(tx3)
   161  	if err != nil {
   162  		t.Fatalf("failed to store tx: %v", err)
   163  	}
   164  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{tx3.WalletTransaction, tx1.WalletTransaction, tx2.WalletTransaction})
   165  	GetTxsAndCheck(2, &tx1.ID, false, []*asset.WalletTransaction{tx3.WalletTransaction, tx1.WalletTransaction})
   166  	GetTxsAndCheck(2, &tx1.ID, true, []*asset.WalletTransaction{tx1.WalletTransaction, tx2.WalletTransaction})
   167  	getPendingTxsAndCheck([]*ExtendedWalletTx{tx3, tx1})
   168  
   169  	err = txHistoryStore.RemoveTx(tx1.ID)
   170  	if err != nil {
   171  		t.Fatalf("failed to remove tx: %v", err)
   172  	}
   173  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{tx3.WalletTransaction, tx2.WalletTransaction})
   174  
   175  	err = txHistoryStore.RemoveTx(tx2.ID)
   176  	if err != nil {
   177  		t.Fatalf("failed to remove tx: %v", err)
   178  	}
   179  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{tx3.WalletTransaction})
   180  
   181  	err = txHistoryStore.RemoveTx(tx3.ID)
   182  	if err != nil {
   183  		t.Fatalf("failed to remove tx: %v", err)
   184  	}
   185  	GetTxsAndCheck(0, nil, true, []*asset.WalletTransaction{})
   186  
   187  	_, err = txHistoryStore.GetTxs(1, &tx2.ID, true)
   188  	if !errors.Is(err, asset.CoinNotFoundError) {
   189  		t.Fatalf("expected coin not found error but got %v", err)
   190  	}
   191  }
   192  
   193  func TestSetAndGetLastQuery(t *testing.T) {
   194  	tempDir := t.TempDir()
   195  	tLogger := dex.StdOutLogger("TXDB", dex.LevelTrace)
   196  
   197  	ctx, cancel := context.WithCancel(context.Background())
   198  	defer cancel()
   199  
   200  	txHistoryStore := NewBadgerTxDB(tempDir, tLogger)
   201  	wg, err := txHistoryStore.Connect(ctx)
   202  	if err != nil {
   203  		t.Fatalf("error connecting to tx history store: %v", err)
   204  	}
   205  	defer func() {
   206  		cancel()
   207  		wg.Wait()
   208  	}()
   209  
   210  	_, err = txHistoryStore.GetLastReceiveTxQuery()
   211  	if !errors.Is(err, ErrNeverQueried) {
   212  		t.Fatalf("Failed to get last query: %v", err)
   213  	}
   214  
   215  	block := uint64(12345)
   216  	err = txHistoryStore.SetLastReceiveTxQuery(block)
   217  	if err != nil {
   218  		t.Fatalf("Failed to set last query: %v", err)
   219  	}
   220  
   221  	lastQuery, err := txHistoryStore.GetLastReceiveTxQuery()
   222  	if err != nil {
   223  		t.Fatalf("Failed to get last query: %v", err)
   224  	}
   225  	if lastQuery != block {
   226  		t.Fatalf("Expected last query to be %d, but got %d", block, lastQuery)
   227  	}
   228  }