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