gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/skylink_test.go (about)

     1  package renter
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"gitlab.com/NebulousLabs/errors"
    10  	"gitlab.com/NebulousLabs/fastrand"
    11  	"gitlab.com/SkynetLabs/skyd/skymodules"
    12  	"go.sia.tech/siad/crypto"
    13  )
    14  
    15  const (
    16  	testSkylink1 = "AABEKWZ_wc2R9qlhYkzbG8mImFVi08kBu1nsvvwPLBtpEg"
    17  	testSkylink2 = "AADxpqE6bH2yFBuCFakOeouCj99CIIKSfgv4B9XsImkxLQ"
    18  )
    19  
    20  var (
    21  	skylink1 skymodules.Skylink
    22  	skylink2 skymodules.Skylink
    23  )
    24  
    25  // TestSkylink probes the skylink manager subsystem.
    26  func TestSkylink(t *testing.T) {
    27  	t.Parallel()
    28  
    29  	// Load Skylinks for tests
    30  	err := skylink1.LoadString(testSkylink1)
    31  	if err != nil {
    32  		t.Fatal(err)
    33  	}
    34  	err = skylink2.LoadString(testSkylink2)
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  
    39  	// Run Tests
    40  	t.Run("Basic", testSkylinkBasic)
    41  	t.Run("IsUnpinned", testIsUnpinned)
    42  }
    43  
    44  // testIsUnpinned probes the handling of checking if a filenode is considered
    45  // unpinned.
    46  func testIsUnpinned(t *testing.T) {
    47  	if testing.Short() {
    48  		t.SkipNow()
    49  	}
    50  
    51  	// Create renter
    52  	rt, err := newRenterTester(t.Name())
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  	defer func() {
    57  		err = rt.Close()
    58  		if err != nil {
    59  			t.Fatal(err)
    60  		}
    61  	}()
    62  
    63  	// create siafile
    64  	sf, err := rt.renter.newRenterTestFile()
    65  	if err != nil {
    66  		t.Fatal(err)
    67  	}
    68  	defer func() {
    69  		err = sf.Close()
    70  		if err != nil {
    71  			t.Fatal(err)
    72  		}
    73  	}()
    74  
    75  	// add link to siafile
    76  	err = sf.AddSkylink(skylink1)
    77  	if err != nil {
    78  		t.Fatal(err)
    79  	}
    80  
    81  	// check isunpinned
    82  	if rt.renter.staticSkylinkManager.callIsUnpinned(sf) {
    83  		t.Error("filenode should not be considered unpinned")
    84  	}
    85  
    86  	// add different link to skylink manager
    87  	rt.renter.staticSkylinkManager.managedAddUnpinRequest(skylink2)
    88  
    89  	// check inunpinned
    90  	if rt.renter.staticSkylinkManager.callIsUnpinned(sf) {
    91  		t.Error("filenode should not be considered unpinned")
    92  	}
    93  
    94  	// add link to skylink manager
    95  	rt.renter.staticSkylinkManager.managedAddUnpinRequest(skylink1)
    96  
    97  	// check isunpinned
    98  	if !rt.renter.staticSkylinkManager.callIsUnpinned(sf) {
    99  		t.Error("filenode should be considered unpinned")
   100  	}
   101  }
   102  
   103  // testSkylinkBasic probes the basic functionality of the skylinkManager
   104  func testSkylinkBasic(t *testing.T) {
   105  	// Initialize new skylinkManager
   106  	sm := newSkylinkManager()
   107  	start := time.Now()
   108  
   109  	// Calling prune on a newly initialized empty skylinkManager should be fine
   110  	sm.callPruneUnpinRequests()
   111  
   112  	// Add skylink
   113  	sm.managedAddUnpinRequest(skylink1)
   114  
   115  	// Define a helper to verify state. This basic test will be adding 1 skylink
   116  	// at a time and we want to make sure that the time is set to be far enough in
   117  	// the future.
   118  	verifyState := func(skylink skymodules.Skylink) error {
   119  		sm.mu.Lock()
   120  		defer sm.mu.Unlock()
   121  		if len(sm.unpinRequests) != 1 {
   122  			return fmt.Errorf("Prune result unexpected; have %v expected %v", len(sm.unpinRequests), 1)
   123  		}
   124  		urt, ok := sm.unpinRequests[skylink.String()]
   125  		if !ok {
   126  			return errors.New("skylink not in unpinRequests")
   127  		}
   128  		if urt.Before(start.Add(2 * TargetHealthCheckFrequency)) {
   129  			return errors.New("time not far enough in the future")
   130  		}
   131  		return nil
   132  	}
   133  
   134  	// Verify state
   135  	err := verifyState(skylink1)
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  
   140  	// Grab the unpinRequest time
   141  	sm.mu.Lock()
   142  	urt := sm.unpinRequests[skylink1.String()]
   143  	sm.mu.Unlock()
   144  
   145  	// Call prune, nothing should happen since the pruneTimeThreshold is still 0
   146  	// so no time is before it.
   147  	sm.callPruneUnpinRequests()
   148  	err = verifyState(skylink1)
   149  	if err != nil {
   150  		t.Fatal(err)
   151  	}
   152  
   153  	// Update the pruneTimeThreshold to now plus 2 * TargetHealthCheckFrequency.
   154  	// This will cause the first skylink to be pruned.
   155  	sm.callUpdatePruneTimeThreshold(time.Now().Add(2 * TargetHealthCheckFrequency))
   156  
   157  	// Add skylink again should be a no-op
   158  	sm.managedAddUnpinRequest(skylink1)
   159  	err = verifyState(skylink1)
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  	sm.mu.Lock()
   164  	urt2 := sm.unpinRequests[skylink1.String()]
   165  	sm.mu.Unlock()
   166  	if !urt.Equal(urt2) {
   167  		t.Error("times shouldn't have been changed")
   168  	}
   169  
   170  	// Add a new skylink
   171  	sm.managedAddUnpinRequest(skylink2)
   172  
   173  	// Call prune, this should prune the original skylink and leave the new
   174  	// skylink.
   175  	sm.callPruneUnpinRequests()
   176  
   177  	// Only the last skylink should be in the unpinRequests
   178  	err = verifyState(skylink2)
   179  	if err != nil {
   180  		t.Fatal(err)
   181  	}
   182  }
   183  
   184  // TestBlocklistHash probes the BlocklistHash method of the renter.
   185  func TestBlocklistHash(t *testing.T) {
   186  	if testing.Short() {
   187  		t.SkipNow()
   188  	}
   189  	t.Parallel()
   190  
   191  	// Create workertester
   192  	wt, err := newWorkerTester(t.Name())
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  	defer func() {
   197  		err = wt.Close()
   198  		if err != nil {
   199  			t.Fatal(err)
   200  		}
   201  	}()
   202  
   203  	// Grab renter
   204  	r := wt.rt.renter
   205  
   206  	// Generate V1 Skylink
   207  	var mr crypto.Hash
   208  	fastrand.Read(mr[:])
   209  	skylinkV1, err := skymodules.NewSkylinkV1(mr, 0, 0)
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  
   214  	// Check hash returned from V1 Skylinks
   215  	hash, err := r.managedBlocklistHash(context.Background(), skylinkV1, false)
   216  	if err != nil {
   217  		t.Fatal(err)
   218  	}
   219  	expected := crypto.HashObject(skylinkV1.MerkleRoot())
   220  	if hash != expected {
   221  		t.Fatal("hashes not equal", hash, expected)
   222  	}
   223  
   224  	// Create a V2 link based on the the V1 link
   225  	//
   226  	// Update the registry with that link.
   227  	srv, spk, sk := randomRegistryValue()
   228  	srv.Data = skylinkV1.Bytes()
   229  	srv.Revision++
   230  	srv = srv.Sign(sk)
   231  	err = wt.UpdateRegistry(context.Background(), spk, srv)
   232  	if err != nil {
   233  		t.Fatal(err)
   234  	}
   235  
   236  	// Get the v2 skylink.
   237  	skylinkV2 := skymodules.NewSkylinkV2(spk, srv.Tweak)
   238  
   239  	// Check V2 link created from V1 link to verify that it also returns the same hash
   240  	hash, err = r.managedBlocklistHash(context.Background(), skylinkV2, false)
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  	if hash != expected {
   245  		t.Fatal("hashes not equal", hash, expected)
   246  	}
   247  }