github.com/decred/dcrlnd@v0.7.6/chainntnfs/height_hint_cache_test.go (about)

     1  package chainntnfs
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"testing"
     7  
     8  	"github.com/decred/dcrd/chaincfg/chainhash"
     9  	"github.com/decred/dcrd/wire"
    10  	"github.com/decred/dcrlnd/channeldb"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func initHintCache(t *testing.T) *HeightHintCache {
    15  	t.Helper()
    16  
    17  	defaultCfg := CacheConfig{
    18  		QueryDisable: false,
    19  	}
    20  
    21  	return initHintCacheWithConfig(t, defaultCfg)
    22  }
    23  
    24  func initHintCacheWithConfig(t *testing.T, cfg CacheConfig) *HeightHintCache {
    25  	t.Helper()
    26  
    27  	tempDir, err := ioutil.TempDir("", "hintcache")
    28  	if err != nil {
    29  		t.Fatalf("unable to create temp dir: %v", err)
    30  	}
    31  	db, err := channeldb.Open(tempDir)
    32  	if err != nil {
    33  		t.Fatalf("unable to create db: %v", err)
    34  	}
    35  	hintCache, err := NewHeightHintCache(cfg, db.Backend)
    36  	if err != nil {
    37  		t.Fatalf("unable to create hint cache: %v", err)
    38  	}
    39  
    40  	return hintCache
    41  }
    42  
    43  // TestHeightHintCacheConfirms ensures that the height hint cache properly
    44  // caches confirm hints for transactions.
    45  func TestHeightHintCacheConfirms(t *testing.T) {
    46  	t.Parallel()
    47  
    48  	hintCache := initHintCache(t)
    49  
    50  	// Querying for a transaction hash not found within the cache should
    51  	// return an error indication so.
    52  	var unknownHash chainhash.Hash
    53  	copy(unknownHash[:], bytes.Repeat([]byte{0x01}, 32))
    54  	unknownConfRequest := ConfRequest{TxID: unknownHash}
    55  	_, err := hintCache.QueryConfirmHint(unknownConfRequest)
    56  	if err != ErrConfirmHintNotFound {
    57  		t.Fatalf("expected ErrConfirmHintNotFound, got: %v", err)
    58  	}
    59  
    60  	// Now, we'll create some transaction hashes and commit them to the
    61  	// cache with the same confirm hint.
    62  	const height = 100
    63  	const numHashes = 5
    64  	confRequests := make([]ConfRequest, numHashes)
    65  	for i := 0; i < numHashes; i++ {
    66  		var txHash chainhash.Hash
    67  		copy(txHash[:], bytes.Repeat([]byte{byte(i + 1)}, 32))
    68  		confRequests[i] = ConfRequest{TxID: txHash}
    69  	}
    70  
    71  	err = hintCache.CommitConfirmHint(height, confRequests...)
    72  	if err != nil {
    73  		t.Fatalf("unable to add entries to cache: %v", err)
    74  	}
    75  
    76  	// With the hashes committed, we'll now query the cache to ensure that
    77  	// we're able to properly retrieve the confirm hints.
    78  	for _, confRequest := range confRequests {
    79  		confirmHint, err := hintCache.QueryConfirmHint(confRequest)
    80  		if err != nil {
    81  			t.Fatalf("unable to query for hint of %v: %v", confRequest, err)
    82  		}
    83  		if confirmHint != height {
    84  			t.Fatalf("expected confirm hint %d, got %d", height,
    85  				confirmHint)
    86  		}
    87  	}
    88  
    89  	// We'll also attempt to purge all of them in a single database
    90  	// transaction.
    91  	if err := hintCache.PurgeConfirmHint(confRequests...); err != nil {
    92  		t.Fatalf("unable to remove confirm hints: %v", err)
    93  	}
    94  
    95  	// Finally, we'll attempt to query for each hash. We should expect not
    96  	// to find a hint for any of them.
    97  	for _, confRequest := range confRequests {
    98  		_, err := hintCache.QueryConfirmHint(confRequest)
    99  		if err != ErrConfirmHintNotFound {
   100  			t.Fatalf("expected ErrConfirmHintNotFound, got :%v", err)
   101  		}
   102  	}
   103  }
   104  
   105  // TestHeightHintCacheSpends ensures that the height hint cache properly caches
   106  // spend hints for outpoints.
   107  func TestHeightHintCacheSpends(t *testing.T) {
   108  	t.Parallel()
   109  
   110  	hintCache := initHintCache(t)
   111  
   112  	// Querying for an outpoint not found within the cache should return an
   113  	// error indication so.
   114  	unknownOutPoint := wire.OutPoint{Index: 1}
   115  	unknownSpendRequest := SpendRequest{OutPoint: unknownOutPoint}
   116  	_, err := hintCache.QuerySpendHint(unknownSpendRequest)
   117  	if err != ErrSpendHintNotFound {
   118  		t.Fatalf("expected ErrSpendHintNotFound, got: %v", err)
   119  	}
   120  
   121  	// Now, we'll create some outpoints and commit them to the cache with
   122  	// the same spend hint.
   123  	const height = 100
   124  	const numOutpoints = 5
   125  	spendRequests := make([]SpendRequest, numOutpoints)
   126  	for i := uint32(0); i < numOutpoints; i++ {
   127  		spendRequests[i] = SpendRequest{
   128  			OutPoint: wire.OutPoint{Index: i + 1},
   129  		}
   130  	}
   131  
   132  	err = hintCache.CommitSpendHint(height, spendRequests...)
   133  	if err != nil {
   134  		t.Fatalf("unable to add entries to cache: %v", err)
   135  	}
   136  
   137  	// With the outpoints committed, we'll now query the cache to ensure
   138  	// that we're able to properly retrieve the confirm hints.
   139  	for _, spendRequest := range spendRequests {
   140  		spendHint, err := hintCache.QuerySpendHint(spendRequest)
   141  		if err != nil {
   142  			t.Fatalf("unable to query for hint: %v", err)
   143  		}
   144  		if spendHint != height {
   145  			t.Fatalf("expected spend hint %d, got %d", height,
   146  				spendHint)
   147  		}
   148  	}
   149  
   150  	// We'll also attempt to purge all of them in a single database
   151  	// transaction.
   152  	if err := hintCache.PurgeSpendHint(spendRequests...); err != nil {
   153  		t.Fatalf("unable to remove spend hint: %v", err)
   154  	}
   155  
   156  	// Finally, we'll attempt to query for each outpoint. We should expect
   157  	// not to find a hint for any of them.
   158  	for _, spendRequest := range spendRequests {
   159  		_, err = hintCache.QuerySpendHint(spendRequest)
   160  		if err != ErrSpendHintNotFound {
   161  			t.Fatalf("expected ErrSpendHintNotFound, got: %v", err)
   162  		}
   163  	}
   164  }
   165  
   166  // TestQueryDisable asserts querying for confirmation or spend hints always
   167  // return height zero when QueryDisabled is set to true in the CacheConfig.
   168  func TestQueryDisable(t *testing.T) {
   169  	cfg := CacheConfig{
   170  		QueryDisable: true,
   171  	}
   172  
   173  	hintCache := initHintCacheWithConfig(t, cfg)
   174  
   175  	// Insert a new confirmation hint with a non-zero height.
   176  	const confHeight = 100
   177  	confRequest := ConfRequest{
   178  		TxID: chainhash.Hash{0x01, 0x02, 0x03},
   179  	}
   180  	err := hintCache.CommitConfirmHint(confHeight, confRequest)
   181  	require.Nil(t, err)
   182  
   183  	// Query for the confirmation hint, which should return zero.
   184  	cachedConfHeight, err := hintCache.QueryConfirmHint(confRequest)
   185  	require.Nil(t, err)
   186  	require.Equal(t, uint32(0), cachedConfHeight)
   187  
   188  	// Insert a new spend hint with a non-zero height.
   189  	const spendHeight = 200
   190  	spendRequest := SpendRequest{
   191  		OutPoint: wire.OutPoint{
   192  			Hash:  chainhash.Hash{0x4, 0x05, 0x06},
   193  			Index: 42,
   194  		},
   195  	}
   196  	err = hintCache.CommitSpendHint(spendHeight, spendRequest)
   197  	require.Nil(t, err)
   198  
   199  	// Query for the spend hint, which should return zero.
   200  	cachedSpendHeight, err := hintCache.QuerySpendHint(spendRequest)
   201  	require.Nil(t, err)
   202  	require.Equal(t, uint32(0), cachedSpendHeight)
   203  }