github.com/Ethersocial/go-esn@v0.3.7/swarm/storage/feed/handler_test.go (about)

     1  // Copyright 2018 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 feed
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"flag"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/ethersocial/go-esn/crypto"
    30  
    31  	"github.com/ethersocial/go-esn/log"
    32  	"github.com/ethersocial/go-esn/swarm/chunk"
    33  	"github.com/ethersocial/go-esn/swarm/storage"
    34  	"github.com/ethersocial/go-esn/swarm/storage/feed/lookup"
    35  )
    36  
    37  var (
    38  	loglevel  = flag.Int("loglevel", 3, "loglevel")
    39  	startTime = Timestamp{
    40  		Time: uint64(4200),
    41  	}
    42  	cleanF       func()
    43  	subtopicName = "føø.bar"
    44  	hashfunc     = storage.MakeHashFunc(storage.DefaultHash)
    45  )
    46  
    47  func init() {
    48  	flag.Parse()
    49  	log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))))
    50  }
    51  
    52  // simulated timeProvider
    53  type fakeTimeProvider struct {
    54  	currentTime uint64
    55  }
    56  
    57  func (f *fakeTimeProvider) Tick() {
    58  	f.currentTime++
    59  }
    60  
    61  func (f *fakeTimeProvider) Set(time uint64) {
    62  	f.currentTime = time
    63  }
    64  
    65  func (f *fakeTimeProvider) FastForward(offset uint64) {
    66  	f.currentTime += offset
    67  }
    68  
    69  func (f *fakeTimeProvider) Now() Timestamp {
    70  	return Timestamp{
    71  		Time: f.currentTime,
    72  	}
    73  }
    74  
    75  // make updates and retrieve them based on periods and versions
    76  func TestFeedsHandler(t *testing.T) {
    77  
    78  	// make fake timeProvider
    79  	clock := &fakeTimeProvider{
    80  		currentTime: startTime.Time, // clock starts at t=4200
    81  	}
    82  
    83  	// signer containing private key
    84  	signer := newAliceSigner()
    85  
    86  	feedsHandler, datadir, teardownTest, err := setupTest(clock, signer)
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  	defer teardownTest()
    91  
    92  	// create a new feed
    93  	ctx, cancel := context.WithCancel(context.Background())
    94  	defer cancel()
    95  
    96  	topic, _ := NewTopic("Mess with Swarm feeds code and see what ghost catches you", nil)
    97  	fd := Feed{
    98  		Topic: topic,
    99  		User:  signer.Address(),
   100  	}
   101  
   102  	// data for updates:
   103  	updates := []string{
   104  		"blinky", // t=4200
   105  		"pinky",  // t=4242
   106  		"inky",   // t=4284
   107  		"clyde",  // t=4285
   108  	}
   109  
   110  	request := NewFirstRequest(fd.Topic) // this timestamps the update at t = 4200 (start time)
   111  	chunkAddress := make(map[string]storage.Address)
   112  	data := []byte(updates[0])
   113  	request.SetData(data)
   114  	if err := request.Sign(signer); err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	chunkAddress[updates[0]], err = feedsHandler.Update(ctx, request)
   118  	if err != nil {
   119  		t.Fatal(err)
   120  	}
   121  
   122  	// move the clock ahead 21 seconds
   123  	clock.FastForward(21) // t=4221
   124  
   125  	request, err = feedsHandler.NewRequest(ctx, &request.Feed) // this timestamps the update at t = 4221
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	if request.Epoch.Base() != 0 || request.Epoch.Level != lookup.HighestLevel-1 {
   130  		t.Fatalf("Suggested epoch BaseTime should be 0 and Epoch level should be %d", lookup.HighestLevel-1)
   131  	}
   132  
   133  	request.Epoch.Level = lookup.HighestLevel // force level 25 instead of 24 to make it fail
   134  	data = []byte(updates[1])
   135  	request.SetData(data)
   136  	if err := request.Sign(signer); err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	chunkAddress[updates[1]], err = feedsHandler.Update(ctx, request)
   140  	if err == nil {
   141  		t.Fatal("Expected update to fail since an update in this epoch already exists")
   142  	}
   143  
   144  	// move the clock ahead 21 seconds
   145  	clock.FastForward(21) // t=4242
   146  	request, err = feedsHandler.NewRequest(ctx, &request.Feed)
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  	request.SetData(data)
   151  	if err := request.Sign(signer); err != nil {
   152  		t.Fatal(err)
   153  	}
   154  	chunkAddress[updates[1]], err = feedsHandler.Update(ctx, request)
   155  	if err != nil {
   156  		t.Fatal(err)
   157  	}
   158  
   159  	// move the clock ahead 42 seconds
   160  	clock.FastForward(42) // t=4284
   161  	request, err = feedsHandler.NewRequest(ctx, &request.Feed)
   162  	if err != nil {
   163  		t.Fatal(err)
   164  	}
   165  	data = []byte(updates[2])
   166  	request.SetData(data)
   167  	if err := request.Sign(signer); err != nil {
   168  		t.Fatal(err)
   169  	}
   170  	chunkAddress[updates[2]], err = feedsHandler.Update(ctx, request)
   171  	if err != nil {
   172  		t.Fatal(err)
   173  	}
   174  
   175  	// move the clock ahead 1 second
   176  	clock.FastForward(1) // t=4285
   177  	request, err = feedsHandler.NewRequest(ctx, &request.Feed)
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  	if request.Epoch.Base() != 0 || request.Epoch.Level != 22 {
   182  		t.Fatalf("Expected epoch base time to be %d, got %d. Expected epoch level to be %d, got %d", 0, request.Epoch.Base(), 22, request.Epoch.Level)
   183  	}
   184  	data = []byte(updates[3])
   185  	request.SetData(data)
   186  
   187  	if err := request.Sign(signer); err != nil {
   188  		t.Fatal(err)
   189  	}
   190  	chunkAddress[updates[3]], err = feedsHandler.Update(ctx, request)
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  
   195  	time.Sleep(time.Second)
   196  	feedsHandler.Close()
   197  
   198  	// check we can retrieve the updates after close
   199  	clock.FastForward(2000) // t=6285
   200  
   201  	feedParams := &HandlerParams{}
   202  
   203  	feedsHandler2, err := NewTestHandler(datadir, feedParams)
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	update2, err := feedsHandler2.Lookup(ctx, NewQueryLatest(&request.Feed, lookup.NoClue))
   209  	if err != nil {
   210  		t.Fatal(err)
   211  	}
   212  
   213  	// last update should be "clyde"
   214  	if !bytes.Equal(update2.data, []byte(updates[len(updates)-1])) {
   215  		t.Fatalf("feed update data was %v, expected %v", string(update2.data), updates[len(updates)-1])
   216  	}
   217  	if update2.Level != 22 {
   218  		t.Fatalf("feed update epoch level was %d, expected 22", update2.Level)
   219  	}
   220  	if update2.Base() != 0 {
   221  		t.Fatalf("feed update epoch base time was %d, expected 0", update2.Base())
   222  	}
   223  	log.Debug("Latest lookup", "epoch base time", update2.Base(), "epoch level", update2.Level, "data", update2.data)
   224  
   225  	// specific point in time
   226  	update, err := feedsHandler2.Lookup(ctx, NewQuery(&request.Feed, 4284, lookup.NoClue))
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	// check data
   231  	if !bytes.Equal(update.data, []byte(updates[2])) {
   232  		t.Fatalf("feed update data (historical) was %v, expected %v", string(update2.data), updates[2])
   233  	}
   234  	log.Debug("Historical lookup", "epoch base time", update2.Base(), "epoch level", update2.Level, "data", update2.data)
   235  
   236  	// beyond the first should yield an error
   237  	update, err = feedsHandler2.Lookup(ctx, NewQuery(&request.Feed, startTime.Time-1, lookup.NoClue))
   238  	if err == nil {
   239  		t.Fatalf("expected previous to fail, returned epoch %s data %v", update.Epoch.String(), update.data)
   240  	}
   241  
   242  }
   243  
   244  const Day = 60 * 60 * 24
   245  const Year = Day * 365
   246  const Month = Day * 30
   247  
   248  func generateData(x uint64) []byte {
   249  	return []byte(fmt.Sprintf("%d", x))
   250  }
   251  
   252  func TestSparseUpdates(t *testing.T) {
   253  
   254  	// make fake timeProvider
   255  	timeProvider := &fakeTimeProvider{
   256  		currentTime: startTime.Time,
   257  	}
   258  
   259  	// signer containing private key
   260  	signer := newAliceSigner()
   261  
   262  	rh, datadir, teardownTest, err := setupTest(timeProvider, signer)
   263  	if err != nil {
   264  		t.Fatal(err)
   265  	}
   266  	defer teardownTest()
   267  	defer os.RemoveAll(datadir)
   268  
   269  	// create a new feed
   270  	ctx, cancel := context.WithCancel(context.Background())
   271  	defer cancel()
   272  	topic, _ := NewTopic("Very slow updates", nil)
   273  	fd := Feed{
   274  		Topic: topic,
   275  		User:  signer.Address(),
   276  	}
   277  
   278  	// publish one update every 5 years since Unix 0 until today
   279  	today := uint64(1533799046)
   280  	var epoch lookup.Epoch
   281  	var lastUpdateTime uint64
   282  	for T := uint64(0); T < today; T += 5 * Year {
   283  		request := NewFirstRequest(fd.Topic)
   284  		request.Epoch = lookup.GetNextEpoch(epoch, T)
   285  		request.data = generateData(T) // this generates some data that depends on T, so we can check later
   286  		request.Sign(signer)
   287  		if err != nil {
   288  			t.Fatal(err)
   289  		}
   290  
   291  		if _, err := rh.Update(ctx, request); err != nil {
   292  			t.Fatal(err)
   293  		}
   294  		epoch = request.Epoch
   295  		lastUpdateTime = T
   296  	}
   297  
   298  	query := NewQuery(&fd, today, lookup.NoClue)
   299  
   300  	_, err = rh.Lookup(ctx, query)
   301  	if err != nil {
   302  		t.Fatal(err)
   303  	}
   304  
   305  	_, content, err := rh.GetContent(&fd)
   306  	if err != nil {
   307  		t.Fatal(err)
   308  	}
   309  
   310  	if !bytes.Equal(generateData(lastUpdateTime), content) {
   311  		t.Fatalf("Expected to recover last written value %d, got %s", lastUpdateTime, string(content))
   312  	}
   313  
   314  	// lookup the closest update to 35*Year + 6* Month (~ June 2005):
   315  	// it should find the update we put on 35*Year, since we were updating every 5 years.
   316  
   317  	query.TimeLimit = 35*Year + 6*Month
   318  
   319  	_, err = rh.Lookup(ctx, query)
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  
   324  	_, content, err = rh.GetContent(&fd)
   325  	if err != nil {
   326  		t.Fatal(err)
   327  	}
   328  
   329  	if !bytes.Equal(generateData(35*Year), content) {
   330  		t.Fatalf("Expected to recover %d, got %s", 35*Year, string(content))
   331  	}
   332  }
   333  
   334  func TestValidator(t *testing.T) {
   335  
   336  	// make fake timeProvider
   337  	timeProvider := &fakeTimeProvider{
   338  		currentTime: startTime.Time,
   339  	}
   340  
   341  	// signer containing private key. Alice will be the good girl
   342  	signer := newAliceSigner()
   343  
   344  	// set up  sim timeProvider
   345  	rh, _, teardownTest, err := setupTest(timeProvider, signer)
   346  	if err != nil {
   347  		t.Fatal(err)
   348  	}
   349  	defer teardownTest()
   350  
   351  	// create new feed
   352  	topic, _ := NewTopic(subtopicName, nil)
   353  	fd := Feed{
   354  		Topic: topic,
   355  		User:  signer.Address(),
   356  	}
   357  	mr := NewFirstRequest(fd.Topic)
   358  
   359  	// chunk with address
   360  	data := []byte("foo")
   361  	mr.SetData(data)
   362  	if err := mr.Sign(signer); err != nil {
   363  		t.Fatalf("sign fail: %v", err)
   364  	}
   365  
   366  	chunk, err := mr.toChunk()
   367  	if err != nil {
   368  		t.Fatal(err)
   369  	}
   370  	if !rh.Validate(chunk.Address(), chunk.Data()) {
   371  		t.Fatal("Chunk validator fail on update chunk")
   372  	}
   373  
   374  	address := chunk.Address()
   375  	// mess with the address
   376  	address[0] = 11
   377  	address[15] = 99
   378  
   379  	if rh.Validate(address, chunk.Data()) {
   380  		t.Fatal("Expected Validate to fail with false chunk address")
   381  	}
   382  }
   383  
   384  // tests that the content address validator correctly checks the data
   385  // tests that feed update chunks are passed through content address validator
   386  // there is some redundancy in this test as it also tests content addressed chunks,
   387  // which should be evaluated as invalid chunks by this validator
   388  func TestValidatorInStore(t *testing.T) {
   389  
   390  	// make fake timeProvider
   391  	TimestampProvider = &fakeTimeProvider{
   392  		currentTime: startTime.Time,
   393  	}
   394  
   395  	// signer containing private key
   396  	signer := newAliceSigner()
   397  
   398  	// set up localstore
   399  	datadir, err := ioutil.TempDir("", "storage-testfeedsvalidator")
   400  	if err != nil {
   401  		t.Fatal(err)
   402  	}
   403  	defer os.RemoveAll(datadir)
   404  
   405  	handlerParams := storage.NewDefaultLocalStoreParams()
   406  	handlerParams.Init(datadir)
   407  	store, err := storage.NewLocalStore(handlerParams, nil)
   408  	if err != nil {
   409  		t.Fatal(err)
   410  	}
   411  
   412  	// set up Swarm feeds handler and add is as a validator to the localstore
   413  	fhParams := &HandlerParams{}
   414  	fh := NewHandler(fhParams)
   415  	store.Validators = append(store.Validators, fh)
   416  
   417  	// create content addressed chunks, one good, one faulty
   418  	chunks := storage.GenerateRandomChunks(chunk.DefaultSize, 2)
   419  	goodChunk := chunks[0]
   420  	badChunk := storage.NewChunk(chunks[1].Address(), goodChunk.Data())
   421  
   422  	topic, _ := NewTopic("xyzzy", nil)
   423  	fd := Feed{
   424  		Topic: topic,
   425  		User:  signer.Address(),
   426  	}
   427  
   428  	// create a feed update chunk with correct publickey
   429  	id := ID{
   430  		Epoch: lookup.Epoch{Time: 42,
   431  			Level: 1,
   432  		},
   433  		Feed: fd,
   434  	}
   435  
   436  	updateAddr := id.Addr()
   437  	data := []byte("bar")
   438  
   439  	r := new(Request)
   440  	r.idAddr = updateAddr
   441  	r.Update.ID = id
   442  	r.data = data
   443  
   444  	r.Sign(signer)
   445  
   446  	uglyChunk, err := r.toChunk()
   447  	if err != nil {
   448  		t.Fatal(err)
   449  	}
   450  
   451  	// put the chunks in the store and check their error status
   452  	err = store.Put(context.Background(), goodChunk)
   453  	if err == nil {
   454  		t.Fatal("expected error on good content address chunk with feed update validator only, but got nil")
   455  	}
   456  	err = store.Put(context.Background(), badChunk)
   457  	if err == nil {
   458  		t.Fatal("expected error on bad content address chunk with feed update validator only, but got nil")
   459  	}
   460  	err = store.Put(context.Background(), uglyChunk)
   461  	if err != nil {
   462  		t.Fatalf("expected no error on feed update chunk with feed update validator only, but got: %s", err)
   463  	}
   464  }
   465  
   466  // create rpc and feeds Handler
   467  func setupTest(timeProvider timestampProvider, signer Signer) (fh *TestHandler, datadir string, teardown func(), err error) {
   468  
   469  	var fsClean func()
   470  	var rpcClean func()
   471  	cleanF = func() {
   472  		if fsClean != nil {
   473  			fsClean()
   474  		}
   475  		if rpcClean != nil {
   476  			rpcClean()
   477  		}
   478  	}
   479  
   480  	// temp datadir
   481  	datadir, err = ioutil.TempDir("", "fh")
   482  	if err != nil {
   483  		return nil, "", nil, err
   484  	}
   485  	fsClean = func() {
   486  		os.RemoveAll(datadir)
   487  	}
   488  
   489  	TimestampProvider = timeProvider
   490  	fhParams := &HandlerParams{}
   491  	fh, err = NewTestHandler(datadir, fhParams)
   492  	return fh, datadir, cleanF, err
   493  }
   494  
   495  func newAliceSigner() *GenericSigner {
   496  	privKey, _ := crypto.HexToECDSA("deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
   497  	return NewGenericSigner(privKey)
   498  }
   499  
   500  func newBobSigner() *GenericSigner {
   501  	privKey, _ := crypto.HexToECDSA("accedeaccedeaccedeaccedeaccedeaccedeaccedeaccedeaccedeaccedecaca")
   502  	return NewGenericSigner(privKey)
   503  }
   504  
   505  func newCharlieSigner() *GenericSigner {
   506  	privKey, _ := crypto.HexToECDSA("facadefacadefacadefacadefacadefacadefacadefacadefacadefacadefaca")
   507  	return NewGenericSigner(privKey)
   508  }
   509  
   510  func getUpdateDirect(rh *Handler, addr storage.Address) ([]byte, error) {
   511  	chunk, err := rh.chunkStore.Get(context.TODO(), addr)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  	var r Request
   516  	if err := r.fromChunk(addr, chunk.Data()); err != nil {
   517  		return nil, err
   518  	}
   519  	return r.data, nil
   520  }