github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/swarm/swarm_test.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package swarm
    18  
    19  import (
    20  	"context"
    21  	"encoding/hex"
    22  	"io/ioutil"
    23  	"math/rand"
    24  	"os"
    25  	"os/exec"
    26  	"path"
    27  	"runtime"
    28  	"strings"
    29  	"testing"
    30  	"time"
    31  
    32  	"github.com/ShyftNetwork/go-empyrean/common"
    33  	"github.com/ShyftNetwork/go-empyrean/crypto"
    34  	"github.com/ShyftNetwork/go-empyrean/rpc"
    35  	"github.com/ShyftNetwork/go-empyrean/swarm/api"
    36  )
    37  
    38  func TestMain(m *testing.M) {
    39  	exec.Command("/bin/sh", "../shyft-cli/shyftTestDbClean.sh")
    40  	retCode := m.Run()
    41  	exec.Command("/bin/sh", "../shyft-cli/shyftTestDbClean.sh")
    42  	os.Exit(retCode)
    43  }
    44  
    45  // TestNewSwarm validates Swarm fields in repsect to the provided configuration.
    46  func TestNewSwarm(t *testing.T) {
    47  	dir, err := ioutil.TempDir("", "swarm")
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  	defer os.RemoveAll(dir)
    52  
    53  	// a simple rpc endpoint for testing dialing
    54  	ipcEndpoint := path.Join(dir, "TestSwarm.ipc")
    55  
    56  	// windows namedpipes are not on filesystem but on NPFS
    57  	if runtime.GOOS == "windows" {
    58  		b := make([]byte, 8)
    59  		rand.Read(b)
    60  		ipcEndpoint = `\\.\pipe\TestSwarm-` + hex.EncodeToString(b)
    61  	}
    62  
    63  	_, server, err := rpc.StartIPCEndpoint(ipcEndpoint, nil)
    64  	if err != nil {
    65  		t.Error(err)
    66  	}
    67  	defer server.Stop()
    68  
    69  	for _, tc := range []struct {
    70  		name      string
    71  		configure func(*api.Config)
    72  		check     func(*testing.T, *Swarm, *api.Config)
    73  	}{
    74  		{
    75  			name:      "defaults",
    76  			configure: nil,
    77  			check: func(t *testing.T, s *Swarm, config *api.Config) {
    78  				if s.config != config {
    79  					t.Error("config is not the same object")
    80  				}
    81  				if s.backend != nil {
    82  					t.Error("backend is not nil")
    83  				}
    84  				if s.privateKey == nil {
    85  					t.Error("private key is not set")
    86  				}
    87  				if !s.config.HiveParams.Discovery {
    88  					t.Error("config.HiveParams.Discovery is false, must be true regardless the configuration")
    89  				}
    90  				if s.dns != nil {
    91  					t.Error("dns initialized, but it should not be")
    92  				}
    93  				if s.netStore == nil {
    94  					t.Error("netStore not initialized")
    95  				}
    96  				if s.streamer == nil {
    97  					t.Error("streamer not initialized")
    98  				}
    99  				if s.fileStore == nil {
   100  					t.Error("fileStore not initialized")
   101  				}
   102  				if s.bzz == nil {
   103  					t.Error("bzz not initialized")
   104  				}
   105  				if s.ps == nil {
   106  					t.Error("pss not initialized")
   107  				}
   108  				if s.api == nil {
   109  					t.Error("api not initialized")
   110  				}
   111  				if s.sfs == nil {
   112  					t.Error("swarm filesystem not initialized")
   113  				}
   114  			},
   115  		},
   116  		{
   117  			name: "with swap",
   118  			configure: func(config *api.Config) {
   119  				config.SwapAPI = ipcEndpoint
   120  				config.SwapEnabled = true
   121  			},
   122  			check: func(t *testing.T, s *Swarm, _ *api.Config) {
   123  				if s.backend == nil {
   124  					t.Error("backend is nil")
   125  				}
   126  			},
   127  		},
   128  		{
   129  			name: "with swap disabled",
   130  			configure: func(config *api.Config) {
   131  				config.SwapAPI = ipcEndpoint
   132  				config.SwapEnabled = false
   133  			},
   134  			check: func(t *testing.T, s *Swarm, _ *api.Config) {
   135  				if s.backend != nil {
   136  					t.Error("backend is not nil")
   137  				}
   138  			},
   139  		},
   140  		{
   141  			name: "with swap enabled and api endpoint blank",
   142  			configure: func(config *api.Config) {
   143  				config.SwapAPI = ""
   144  				config.SwapEnabled = true
   145  			},
   146  			check: func(t *testing.T, s *Swarm, _ *api.Config) {
   147  				if s.backend != nil {
   148  					t.Error("backend is not nil")
   149  				}
   150  			},
   151  		},
   152  		{
   153  			name: "ens",
   154  			configure: func(config *api.Config) {
   155  				config.EnsAPIs = []string{
   156  					"http://127.0.0.1:8888",
   157  				}
   158  			},
   159  			check: func(t *testing.T, s *Swarm, _ *api.Config) {
   160  				if s.dns == nil {
   161  					t.Error("dns is not initialized")
   162  				}
   163  			},
   164  		},
   165  	} {
   166  		t.Run(tc.name, func(t *testing.T) {
   167  			config := api.NewConfig()
   168  
   169  			dir, err := ioutil.TempDir("", "swarm")
   170  			if err != nil {
   171  				t.Fatal(err)
   172  			}
   173  			defer os.RemoveAll(dir)
   174  
   175  			config.Path = dir
   176  
   177  			privkey, err := crypto.GenerateKey()
   178  			if err != nil {
   179  				t.Fatal(err)
   180  			}
   181  
   182  			config.Init(privkey)
   183  
   184  			if tc.configure != nil {
   185  				tc.configure(config)
   186  			}
   187  
   188  			s, err := NewSwarm(config, nil)
   189  			if err != nil {
   190  				t.Fatal(err)
   191  			}
   192  
   193  			if tc.check != nil {
   194  				tc.check(t, s, config)
   195  			}
   196  		})
   197  	}
   198  }
   199  
   200  func TestParseEnsAPIAddress(t *testing.T) {
   201  	for _, x := range []struct {
   202  		description string
   203  		value       string
   204  		tld         string
   205  		endpoint    string
   206  		addr        common.Address
   207  	}{
   208  		{
   209  			description: "IPC endpoint",
   210  			value:       "/data/testnet/geth.ipc",
   211  			endpoint:    "/data/testnet/geth.ipc",
   212  		},
   213  		{
   214  			description: "HTTP endpoint",
   215  			value:       "http://127.0.0.1:1234",
   216  			endpoint:    "http://127.0.0.1:1234",
   217  		},
   218  		{
   219  			description: "WS endpoint",
   220  			value:       "ws://127.0.0.1:1234",
   221  			endpoint:    "ws://127.0.0.1:1234",
   222  		},
   223  		{
   224  			description: "IPC Endpoint and TLD",
   225  			value:       "test:/data/testnet/geth.ipc",
   226  			endpoint:    "/data/testnet/geth.ipc",
   227  			tld:         "test",
   228  		},
   229  		{
   230  			description: "HTTP endpoint and TLD",
   231  			value:       "test:http://127.0.0.1:1234",
   232  			endpoint:    "http://127.0.0.1:1234",
   233  			tld:         "test",
   234  		},
   235  		{
   236  			description: "WS endpoint and TLD",
   237  			value:       "test:ws://127.0.0.1:1234",
   238  			endpoint:    "ws://127.0.0.1:1234",
   239  			tld:         "test",
   240  		},
   241  		{
   242  			description: "IPC Endpoint and contract address",
   243  			value:       "314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
   244  			endpoint:    "/data/testnet/geth.ipc",
   245  			addr:        common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
   246  		},
   247  		{
   248  			description: "HTTP endpoint and contract address",
   249  			value:       "314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
   250  			endpoint:    "http://127.0.0.1:1234",
   251  			addr:        common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
   252  		},
   253  		{
   254  			description: "WS endpoint and contract address",
   255  			value:       "314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:1234",
   256  			endpoint:    "ws://127.0.0.1:1234",
   257  			addr:        common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
   258  		},
   259  		{
   260  			description: "IPC Endpoint, TLD and contract address",
   261  			value:       "test:314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
   262  			endpoint:    "/data/testnet/geth.ipc",
   263  			addr:        common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
   264  			tld:         "test",
   265  		},
   266  		{
   267  			description: "HTTP endpoint, TLD and contract address",
   268  			value:       "eth:314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
   269  			endpoint:    "http://127.0.0.1:1234",
   270  			addr:        common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
   271  			tld:         "eth",
   272  		},
   273  		{
   274  			description: "WS endpoint, TLD and contract address",
   275  			value:       "eth:314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:1234",
   276  			endpoint:    "ws://127.0.0.1:1234",
   277  			addr:        common.HexToAddress("314159265dD8dbb310642f98f50C066173C1259b"),
   278  			tld:         "eth",
   279  		},
   280  	} {
   281  		t.Run(x.description, func(t *testing.T) {
   282  			tld, endpoint, addr := parseEnsAPIAddress(x.value)
   283  			if endpoint != x.endpoint {
   284  				t.Errorf("expected Endpoint %q, got %q", x.endpoint, endpoint)
   285  			}
   286  			if addr != x.addr {
   287  				t.Errorf("expected ContractAddress %q, got %q", x.addr.String(), addr.String())
   288  			}
   289  			if tld != x.tld {
   290  				t.Errorf("expected TLD %q, got %q", x.tld, tld)
   291  			}
   292  		})
   293  	}
   294  }
   295  
   296  // TestLocalStoreAndRetrieve runs multiple tests where different size files are uploaded
   297  // to a single Swarm instance using API Store and checked against the content returned
   298  // by API Retrieve function.
   299  //
   300  // This test is intended to validate functionality of chunker store and join functions
   301  // and their intergartion into Swarm, without comparing results with ones produced by
   302  // another chunker implementation, as it is done in swarm/storage tests.
   303  func TestLocalStoreAndRetrieve(t *testing.T) {
   304  	config := api.NewConfig()
   305  
   306  	dir, err := ioutil.TempDir("", "node")
   307  	if err != nil {
   308  		t.Fatal(err)
   309  	}
   310  	defer os.RemoveAll(dir)
   311  
   312  	config.Path = dir
   313  
   314  	privkey, err := crypto.GenerateKey()
   315  	if err != nil {
   316  		t.Fatal(err)
   317  	}
   318  
   319  	config.Init(privkey)
   320  
   321  	swarm, err := NewSwarm(config, nil)
   322  	if err != nil {
   323  		t.Fatal(err)
   324  	}
   325  
   326  	// by default, test only the lonely chunk cases
   327  	sizes := []int{1, 60, 4097, 524288 + 1, 7*524288 + 1}
   328  
   329  	if *longrunning {
   330  		// test broader set of cases if -longruning flag is set
   331  		sizes = append(sizes, 83, 179, 253, 1024, 4095, 4096, 8191, 8192, 8193, 12287, 12288, 12289, 123456, 2345678, 67298391, 524288, 524288+4096, 524288+4097, 7*524288, 7*524288+4096, 7*524288+4097, 128*524288+1, 128*524288, 128*524288+4096, 128*524288+4097, 816778334)
   332  	}
   333  	for _, n := range sizes {
   334  		testLocalStoreAndRetrieve(t, swarm, n, true)
   335  		testLocalStoreAndRetrieve(t, swarm, n, false)
   336  	}
   337  }
   338  
   339  // testLocalStoreAndRetrieve is using a single Swarm instance, to upload
   340  // a file of length n with optional random data using API Store function,
   341  // and checks the output of API Retrieve function on the same instance.
   342  // This is a regression test for issue
   343  // https://github.com/ethersphere/go-ethereum/issues/639
   344  // where pyramid chunker did not split correctly files with lengths that
   345  // are edge cases for chunk and tree parameters, depending whether there
   346  // is a tree chunk with only one data chunk and how the compress functionality
   347  // changed the tree.
   348  func testLocalStoreAndRetrieve(t *testing.T, swarm *Swarm, n int, randomData bool) {
   349  	slice := make([]byte, n)
   350  	if randomData {
   351  		rand.Seed(time.Now().UnixNano())
   352  		rand.Read(slice)
   353  	}
   354  	dataPut := string(slice)
   355  
   356  	ctx := context.TODO()
   357  	k, wait, err := swarm.api.Store(ctx, strings.NewReader(dataPut), int64(len(dataPut)), false)
   358  	if err != nil {
   359  		t.Fatal(err)
   360  	}
   361  	if wait != nil {
   362  		err = wait(ctx)
   363  		if err != nil {
   364  			t.Fatal(err)
   365  		}
   366  	}
   367  
   368  	r, _ := swarm.api.Retrieve(context.TODO(), k)
   369  
   370  	d, err := ioutil.ReadAll(r)
   371  	if err != nil {
   372  		t.Fatal(err)
   373  	}
   374  	dataGet := string(d)
   375  
   376  	if len(dataPut) != len(dataGet) {
   377  		t.Fatalf("data not matched: length expected %v, got %v", len(dataPut), len(dataGet))
   378  	} else {
   379  		if dataPut != dataGet {
   380  			t.Fatal("data not matched")
   381  		}
   382  	}
   383  }