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

     1  package skynetportals
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  
    10  	"gitlab.com/NebulousLabs/errors"
    11  	"gitlab.com/NebulousLabs/fastrand"
    12  	"gitlab.com/SkynetLabs/skyd/build"
    13  	"gitlab.com/SkynetLabs/skyd/skymodules"
    14  	"go.sia.tech/siad/modules"
    15  	"go.sia.tech/siad/persist"
    16  )
    17  
    18  // testDir is a helper function for creating the testing directory
    19  func testDir(name string) string {
    20  	return build.TempDir("skynetportals", name)
    21  }
    22  
    23  // checkNumPersistedPortals checks that the expected number of portals has been
    24  // persisted on disk by checking the size of the persistence file.
    25  func checkNumPersistedPortals(portalsPath string, numPortals int) error {
    26  	expectedSize := numPortals*int(persistSize) + int(persist.MetadataPageSize)
    27  	if fi, err := os.Stat(portalsPath); err != nil {
    28  		return errors.AddContext(err, "failed to get portal list filesize")
    29  	} else if fi.Size() != int64(expectedSize) {
    30  		return fmt.Errorf("expected %v portals to have a filesize of %v but was %v", numPortals, expectedSize, fi.Size())
    31  	}
    32  	return nil
    33  }
    34  
    35  // TestPersist tests the persistence of the Skynet portals list.
    36  func TestPersist(t *testing.T) {
    37  	if testing.Short() {
    38  		t.SkipNow()
    39  	}
    40  	t.Parallel()
    41  
    42  	// Create a new SkynetPortals
    43  	testdir := testDir(t.Name())
    44  	pl, err := New(testdir)
    45  	if err != nil {
    46  		t.Fatal(err)
    47  	}
    48  
    49  	filename := filepath.Join(testdir, persistFile)
    50  	if filename != pl.staticAop.FilePath() {
    51  		t.Fatalf("Expected filepath %v, was %v", filename, pl.staticAop.FilePath())
    52  	}
    53  
    54  	// There should be no portals in the list
    55  	if len(pl.portals) != 0 {
    56  		t.Fatal("Expected portals list to be empty but found:", len(pl.portals))
    57  	}
    58  
    59  	// Update portals list
    60  	portal := skymodules.SkynetPortal{
    61  		Address: "localhost:9980",
    62  		Public:  true,
    63  	}
    64  	add := []skymodules.SkynetPortal{portal}
    65  	remove := []modules.NetAddress{portal.Address}
    66  	err = pl.UpdatePortals(add, remove)
    67  	if err != nil {
    68  		t.Fatal(err)
    69  	}
    70  
    71  	// Portals list should be empty because we added and then removed the same
    72  	// portal
    73  	if len(pl.portals) != 0 {
    74  		t.Fatal("Expected portals list to be empty but found:", len(pl.portals))
    75  	}
    76  
    77  	// Verify that the correct number of portals were persisted to verify no
    78  	// portals are being truncated
    79  	if err := checkNumPersistedPortals(filename, 2); err != nil {
    80  		t.Errorf("error verifying correct number of portals: %v", err)
    81  	}
    82  
    83  	// Add the portal again
    84  	err = pl.UpdatePortals(add, []modules.NetAddress{})
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  
    89  	// There should be 1 element in the portals list now
    90  	if len(pl.portals) != 1 {
    91  		t.Fatal("Expected 1 element in the portals list but found:", len(pl.portals))
    92  	}
    93  	public, ok := pl.portals[portal.Address]
    94  	if public != portal.Public {
    95  		t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public)
    96  	}
    97  	if !ok {
    98  		t.Fatalf("Expected address %v to be listed in portals list", portal.Address)
    99  	}
   100  
   101  	// Load a new Skynet Portals List to verify the contents from disk get loaded
   102  	// properly
   103  	pl2, err := New(testdir)
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  
   108  	// Verify that the correct number of portals were persisted to verify no
   109  	// portals are being truncated
   110  	if err := checkNumPersistedPortals(filename, 3); err != nil {
   111  		t.Fatalf("error verifying correct number of portals: %v", err)
   112  	}
   113  
   114  	// There should be 1 element in the portals list
   115  	if len(pl2.portals) != 1 {
   116  		t.Fatal("Expected 1 element in the portals list but found:", len(pl2.portals))
   117  	}
   118  	public, ok = pl2.portals[portal.Address]
   119  	if public != portal.Public {
   120  		t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public)
   121  	}
   122  	if !ok {
   123  		t.Fatalf("Expected address %v to be listed in portals list", portal.Address)
   124  	}
   125  
   126  	// Add the portal again
   127  	err = pl2.UpdatePortals(add, []modules.NetAddress{})
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  
   132  	// There should still only be 1 element in the portal list
   133  	if len(pl2.portals) != 1 {
   134  		t.Fatal("Expected 1 element in the portal list but found:", len(pl2.portals))
   135  	}
   136  	public, ok = pl2.portals[portal.Address]
   137  	if public != portal.Public {
   138  		t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public)
   139  	}
   140  	if !ok {
   141  		t.Fatalf("Expected address %v to be listed in portals list", portal.Address)
   142  	}
   143  
   144  	// Load another new Skynet Portals List to verify the contents from disk get
   145  	// loaded properly
   146  	pl3, err := New(testdir)
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	// Verify that the correct number of portals were persisted to verify no
   152  	// portals are being truncated
   153  	if err := checkNumPersistedPortals(filename, 4); err != nil {
   154  		t.Fatalf("error verifying correct number of portals: %v", err)
   155  	}
   156  
   157  	// There should be 1 element in the portals list
   158  	if len(pl3.portals) != 1 {
   159  		t.Fatal("Expected 1 element in the portals list but found:", len(pl3.portals))
   160  	}
   161  	public, ok = pl3.portals[portal.Address]
   162  	if !ok {
   163  		t.Fatalf("Expected address %v to be listed in portals list", portal.Address)
   164  	}
   165  }
   166  
   167  // TestPersistCorruption tests the persistence of the Skynet portal list when
   168  // corruption occurs.
   169  func TestPersistCorruption(t *testing.T) {
   170  	if testing.Short() {
   171  		t.SkipNow()
   172  	}
   173  	t.Parallel()
   174  
   175  	// Create a new SkynetPortalList
   176  	testdir := testDir(t.Name())
   177  	pl, err := New(testdir)
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  
   182  	filename := filepath.Join(testdir, persistFile)
   183  	if filename != pl.staticAop.FilePath() {
   184  		t.Fatalf("Expected filepath %v, was %v", filename, pl.staticAop.FilePath())
   185  	}
   186  
   187  	// There should be no portals in the list
   188  	if len(pl.portals) != 0 {
   189  		t.Fatal("Expected portals list to be empty but found:", len(pl.portals))
   190  	}
   191  
   192  	// Append a bunch of random data to the end of the portals list file to test
   193  	// corruption
   194  	f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, skymodules.DefaultFilePerm)
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  	minNumBytes := int(2 * persist.MetadataPageSize)
   199  	_, err = f.Write(fastrand.Bytes(minNumBytes + fastrand.Intn(minNumBytes)))
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	err = f.Close()
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	// The filesize with corruption should be greater than the persist length.
   209  	fi, err := os.Stat(filename)
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  	filesize := fi.Size()
   214  	if uint64(filesize) <= pl.staticAop.PersistLength() {
   215  		t.Fatalf("Expected file size greater than %v, got %v", pl.staticAop.PersistLength(), filesize)
   216  	}
   217  
   218  	// Update portals list
   219  	portal := skymodules.SkynetPortal{
   220  		Address: "localhost:9980",
   221  		Public:  true,
   222  	}
   223  	add := []skymodules.SkynetPortal{portal}
   224  	remove := []modules.NetAddress{portal.Address}
   225  	err = pl.UpdatePortals(add, remove)
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  
   230  	// The filesize should be equal to the persist length now due to the
   231  	// truncate when updating.
   232  	fi, err = os.Stat(filename)
   233  	if err != nil {
   234  		t.Fatal(err)
   235  	}
   236  	filesize = fi.Size()
   237  	if uint64(filesize) != pl.staticAop.PersistLength() {
   238  		t.Fatalf("Expected file size %v, got %v", pl.staticAop.PersistLength(), filesize)
   239  	}
   240  
   241  	// Portals list should be empty because we added and then removed the same
   242  	// portal
   243  	if len(pl.portals) != 0 {
   244  		t.Fatal("Expected portals list to be empty but found:", len(pl.portals))
   245  	}
   246  
   247  	// Add the portal again
   248  	err = pl.UpdatePortals(add, []modules.NetAddress{})
   249  	if err != nil {
   250  		t.Fatal(err)
   251  	}
   252  
   253  	// There should be 1 element in the portals list now
   254  	if len(pl.portals) != 1 {
   255  		t.Fatal("Expected 1 element in the portals list but found:", len(pl.portals))
   256  	}
   257  	public, ok := pl.portals[portal.Address]
   258  	if public != portal.Public {
   259  		t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public)
   260  	}
   261  	if !ok {
   262  		t.Fatalf("Expected address %v to be listed in portals list", portal.Address)
   263  	}
   264  
   265  	// Load a new Skynet Portals List to verify the contents from disk get loaded
   266  	// properly
   267  	pl2, err := New(testdir)
   268  	if err != nil {
   269  		t.Fatal(err)
   270  	}
   271  
   272  	// There should be 1 element in the portals list
   273  	if len(pl2.portals) != 1 {
   274  		t.Fatal("Expected 1 element in the portals list but found:", len(pl2.portals))
   275  	}
   276  	public, ok = pl2.portals[portal.Address]
   277  	if public != portal.Public {
   278  		t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public)
   279  	}
   280  	if !ok {
   281  		t.Fatalf("Expected address %v to be listed in portals list", portal.Address)
   282  	}
   283  
   284  	// Add the portal again
   285  	err = pl2.UpdatePortals(add, []modules.NetAddress{})
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  
   290  	// There should still only be 1 element in the portal list
   291  	if len(pl2.portals) != 1 {
   292  		t.Fatal("Expected 1 element in the portal list but found:", len(pl2.portals))
   293  	}
   294  	public, ok = pl2.portals[portal.Address]
   295  	if public != portal.Public {
   296  		t.Fatalf("Expected publicness of portal listed in portals list to be %v but was %v", portal.Public, public)
   297  	}
   298  	if !ok {
   299  		t.Fatalf("Expected address %v to be listed in portals list", portal.Address)
   300  	}
   301  
   302  	// Load another new Skynet Portals List to verify the contents from disk get
   303  	// loaded properly
   304  	pl3, err := New(testdir)
   305  	if err != nil {
   306  		t.Fatal(err)
   307  	}
   308  
   309  	// There should be 1 element in the portals list
   310  	if len(pl3.portals) != 1 {
   311  		t.Fatal("Expected 1 element in the portals list but found:", len(pl3.portals))
   312  	}
   313  	public, ok = pl3.portals[portal.Address]
   314  	if !ok {
   315  		t.Fatalf("Expected address %v to be listed in portals list", portal.Address)
   316  	}
   317  
   318  	// The final filesize should be equal to the persist length.
   319  	fi, err = os.Stat(filename)
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  	filesize = fi.Size()
   324  	if uint64(filesize) != pl3.staticAop.PersistLength() {
   325  		t.Fatalf("Expected file size %v, got %v", pl3.staticAop.PersistLength(), filesize)
   326  	}
   327  
   328  	// Verify that the correct number of portals were persisted to verify no
   329  	// portals are being truncated
   330  	if err := checkNumPersistedPortals(filename, 4); err != nil {
   331  		t.Fatalf("error verifying correct number of portals: %v", err)
   332  	}
   333  }
   334  
   335  // TestMarshalSia probes the marshalSia and unmarshalSia methods
   336  func TestMarshalSia(t *testing.T) {
   337  	// Test MarshalSia
   338  	portal := skymodules.SkynetPortal{
   339  		Address: modules.NetAddress("localhost:9980"),
   340  		Public:  true,
   341  	}
   342  	var buf bytes.Buffer
   343  	address := portal.Address
   344  	listed := false
   345  	public := portal.Public
   346  	pe := persistEntry{address, public, listed}
   347  	err := pe.MarshalSia(&buf)
   348  	if err != nil {
   349  		t.Fatal(err)
   350  	}
   351  	pe.listed = true
   352  	err = pe.MarshalSia(&buf)
   353  	if err != nil {
   354  		t.Fatal(err)
   355  	}
   356  
   357  	// Test UnmarshalSia, portals should unmarshal in the order they were
   358  	// marshalled.
   359  	r := bytes.NewBuffer(buf.Bytes())
   360  	err = pe.UnmarshalSia(r)
   361  	if err != nil {
   362  		t.Fatal(err)
   363  	}
   364  	if address != pe.address {
   365  		t.Fatalf("Addresses don't match, expected %v, got %v", address, pe.address)
   366  	}
   367  	if public != pe.public {
   368  		t.Fatalf("Publicness doesn't match, expected %v, got %v", public, pe.public)
   369  	}
   370  	if pe.listed {
   371  		t.Fatal("expected persisted portal to not be listed")
   372  	}
   373  	err = pe.UnmarshalSia(r)
   374  	if err != nil {
   375  		t.Fatal(err)
   376  	}
   377  	if public != pe.public {
   378  		t.Fatalf("Publicness doesn't match, expected %v, got %v", public, pe.public)
   379  	}
   380  	if address != pe.address {
   381  		t.Fatalf("Addresses don't match, expected %v, got %v", address, pe.address)
   382  	}
   383  	if !pe.listed {
   384  		t.Fatal("expected persisted portal to be listed")
   385  	}
   386  
   387  	// Test unmarshalPersistPortals
   388  	portals, err := unmarshalObjects(&buf)
   389  	if err != nil {
   390  		t.Fatal(err)
   391  	}
   392  
   393  	// Since the address is the same the portals list should only have a length
   394  	// of 1 since the non listed address was added first.
   395  	if len(portals) != 1 {
   396  		t.Fatalf("Incorrect number of listed addresses, expected %v, got %v", 1, len(portals))
   397  	}
   398  	_, ok := portals[address]
   399  	if !ok {
   400  		t.Fatal("address not found in portals list")
   401  	}
   402  }