mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
swarm/testutil: Testing framework demo
This commit is contained in:
parent
472c23a801
commit
a5af8afd89
13 changed files with 256 additions and 397 deletions
|
|
@ -17,12 +17,12 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
|
||||
"github.com/ethereum/go-ethereum/swarm/api"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
||||
|
|
@ -32,33 +32,21 @@ type TestServer interface {
|
|||
ServeHTTP(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
func NewTestSwarmServer(t *testing.T, serverFunc func(*api.API) TestServer, resolver api.Resolver) *TestSwarmServer {
|
||||
dir, err := ioutil.TempDir("", "swarm-storage-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
func NewTestSwarmServer(tx *testing.T, serverFunc func(*api.API) TestServer, resolver api.Resolver) *TestSwarmServer {
|
||||
// hacked this function for the testing framework demo. Disregard changes here.
|
||||
t := testutil.BeginTest(tx, false)
|
||||
dir := t.Services.NewTempDir()
|
||||
|
||||
storeparams := storage.NewDefaultLocalStoreParams()
|
||||
storeparams.DbCapacity = 5000000
|
||||
storeparams.CacheCapacity = 5000
|
||||
storeparams.Init(dir)
|
||||
localStore, err := storage.NewLocalStore(storeparams, nil)
|
||||
if err != nil {
|
||||
os.RemoveAll(dir)
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
fileStore := storage.NewFileStore(localStore, storage.NewFileStoreParams())
|
||||
|
||||
// Swarm feeds test setup
|
||||
feedsDir, err := ioutil.TempDir("", "swarm-feeds-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rhparams := &feed.HandlerParams{}
|
||||
rh, err := feed.NewTestHandler(feedsDir, rhparams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rh := feed.NewTestHandler(t, "")
|
||||
|
||||
a := api.NewAPI(fileStore, resolver, rh.Handler, nil)
|
||||
srv := httptest.NewServer(serverFunc(a))
|
||||
|
|
@ -70,8 +58,7 @@ func NewTestSwarmServer(t *testing.T, serverFunc func(*api.API) TestServer, reso
|
|||
cleanup: func() {
|
||||
srv.Close()
|
||||
rh.Close()
|
||||
os.RemoveAll(dir)
|
||||
os.RemoveAll(feedsDir)
|
||||
t.FinishTest()
|
||||
},
|
||||
CurrentTime: 42,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@
|
|||
package feed
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -31,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/swarm/chunk"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -71,7 +70,9 @@ func (f *fakeTimeProvider) Now() Timestamp {
|
|||
}
|
||||
|
||||
// make updates and retrieve them based on periods and versions
|
||||
func TestFeedsHandler(t *testing.T) {
|
||||
func TestFeedsHandler(tx *testing.T) {
|
||||
t := testutil.BeginTest(tx, false) // set to true to generate results
|
||||
defer t.FinishTest()
|
||||
|
||||
// make fake timeProvider
|
||||
clock := &fakeTimeProvider{
|
||||
|
|
@ -80,12 +81,7 @@ func TestFeedsHandler(t *testing.T) {
|
|||
|
||||
// signer containing private key
|
||||
signer := newAliceSigner()
|
||||
|
||||
feedsHandler, datadir, teardownTest, err := setupTest(clock, signer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer teardownTest()
|
||||
feedsHandler := setupTest(t, clock)
|
||||
|
||||
// create a new feed
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
|
@ -109,86 +105,68 @@ func TestFeedsHandler(t *testing.T) {
|
|||
chunkAddress := make(map[string]storage.Address)
|
||||
data := []byte(updates[0])
|
||||
request.SetData(data)
|
||||
if err := request.Sign(signer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := request.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
chunkAddress[updates[0]], err = feedsHandler.Update(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
// move the clock ahead 21 seconds
|
||||
clock.FastForward(21) // t=4221
|
||||
|
||||
request, err = feedsHandler.NewRequest(ctx, &request.Feed) // this timestamps the update at t = 4221
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if request.Epoch.Base() != 0 || request.Epoch.Level != lookup.HighestLevel-1 {
|
||||
t.Fatalf("Suggested epoch BaseTime should be 0 and Epoch level should be %d", lookup.HighestLevel-1)
|
||||
}
|
||||
t.Ok(err)
|
||||
t.Assert(request.Epoch.Base() == 0 && request.Epoch.Level == lookup.HighestLevel-1, "Suggested epoch BaseTime should be 0 and Epoch level should be %d", lookup.HighestLevel-1)
|
||||
|
||||
request.Epoch.Level = lookup.HighestLevel // force level 25 instead of 24 to make it fail
|
||||
data = []byte(updates[1])
|
||||
request.SetData(data)
|
||||
if err := request.Sign(signer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = request.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
chunkAddress[updates[1]], err = feedsHandler.Update(ctx, request)
|
||||
if err == nil {
|
||||
t.Fatal("Expected update to fail since an update in this epoch already exists")
|
||||
}
|
||||
t.MustFail(err, "Expected update to fail since an update in this epoch already exists")
|
||||
|
||||
// move the clock ahead 21 seconds
|
||||
clock.FastForward(21) // t=4242
|
||||
request, err = feedsHandler.NewRequest(ctx, &request.Feed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
request.SetData(data)
|
||||
if err := request.Sign(signer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = request.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
chunkAddress[updates[1]], err = feedsHandler.Update(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
// move the clock ahead 42 seconds
|
||||
clock.FastForward(42) // t=4284
|
||||
request, err = feedsHandler.NewRequest(ctx, &request.Feed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
data = []byte(updates[2])
|
||||
request.SetData(data)
|
||||
if err := request.Sign(signer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = request.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
chunkAddress[updates[2]], err = feedsHandler.Update(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
// move the clock ahead 1 second
|
||||
clock.FastForward(1) // t=4285
|
||||
request, err = feedsHandler.NewRequest(ctx, &request.Feed)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if request.Epoch.Base() != 0 || request.Epoch.Level != 22 {
|
||||
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)
|
||||
}
|
||||
t.Ok(err)
|
||||
t.Assert(request.Epoch.Base() == 0 && request.Epoch.Level == 22, "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)
|
||||
|
||||
data = []byte(updates[3])
|
||||
request.SetData(data)
|
||||
|
||||
if err := request.Sign(signer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = request.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
chunkAddress[updates[3]], err = feedsHandler.Update(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
time.Sleep(time.Second)
|
||||
feedsHandler.Close()
|
||||
|
|
@ -196,47 +174,29 @@ func TestFeedsHandler(t *testing.T) {
|
|||
// check we can retrieve the updates after close
|
||||
clock.FastForward(2000) // t=6285
|
||||
|
||||
feedParams := &HandlerParams{}
|
||||
|
||||
feedsHandler2, err := NewTestHandler(datadir, feedParams)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
feedsHandler2 := NewTestHandler(t, feedsHandler.dataDir)
|
||||
t.Ok(err)
|
||||
|
||||
update2, err := feedsHandler2.Lookup(ctx, NewQueryLatest(&request.Feed, lookup.NoClue))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
// last update should be "clyde"
|
||||
if !bytes.Equal(update2.data, []byte(updates[len(updates)-1])) {
|
||||
t.Fatalf("feed update data was %v, expected %v", string(update2.data), updates[len(updates)-1])
|
||||
}
|
||||
if update2.Level != 22 {
|
||||
t.Fatalf("feed update epoch level was %d, expected 22", update2.Level)
|
||||
}
|
||||
if update2.Base() != 0 {
|
||||
t.Fatalf("feed update epoch base time was %d, expected 0", update2.Base())
|
||||
}
|
||||
t.Equals([]byte(updates[len(updates)-1]), update2.data)
|
||||
t.Assert(update2.Level == 22, "feed update epoch level was %d, expected 22", update2.Level)
|
||||
t.Assert(update2.Base() == 0, "feed update epoch base time was %d, expected 0", update2.Base())
|
||||
|
||||
log.Debug("Latest lookup", "epoch base time", update2.Base(), "epoch level", update2.Level, "data", update2.data)
|
||||
|
||||
// specific point in time
|
||||
update, err := feedsHandler2.Lookup(ctx, NewQuery(&request.Feed, 4284, lookup.NoClue))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// check data
|
||||
if !bytes.Equal(update.data, []byte(updates[2])) {
|
||||
t.Fatalf("feed update data (historical) was %v, expected %v", string(update2.data), updates[2])
|
||||
}
|
||||
t.Ok(err)
|
||||
t.Equals([]byte(updates[2]), update.data)
|
||||
|
||||
log.Debug("Historical lookup", "epoch base time", update2.Base(), "epoch level", update2.Level, "data", update2.data)
|
||||
|
||||
// beyond the first should yield an error
|
||||
update, err = feedsHandler2.Lookup(ctx, NewQuery(&request.Feed, startTime.Time-1, lookup.NoClue))
|
||||
if err == nil {
|
||||
t.Fatalf("expected previous to fail, returned epoch %s data %v", update.Epoch.String(), update.data)
|
||||
}
|
||||
|
||||
t.MustFail(err, "expected previous to fail")
|
||||
}
|
||||
|
||||
const Day = 60 * 60 * 24
|
||||
|
|
@ -247,7 +207,9 @@ func generateData(x uint64) []byte {
|
|||
return []byte(fmt.Sprintf("%d", x))
|
||||
}
|
||||
|
||||
func TestSparseUpdates(t *testing.T) {
|
||||
func TestSparseUpdates(tx *testing.T) {
|
||||
t := testutil.BeginTest(tx, false) // set to true to generate results
|
||||
defer t.FinishTest()
|
||||
|
||||
// make fake timeProvider
|
||||
timeProvider := &fakeTimeProvider{
|
||||
|
|
@ -256,13 +218,7 @@ func TestSparseUpdates(t *testing.T) {
|
|||
|
||||
// signer containing private key
|
||||
signer := newAliceSigner()
|
||||
|
||||
rh, datadir, teardownTest, err := setupTest(timeProvider, signer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer teardownTest()
|
||||
defer os.RemoveAll(datadir)
|
||||
rh := setupTest(t, timeProvider)
|
||||
|
||||
// create a new feed
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
|
@ -281,33 +237,24 @@ func TestSparseUpdates(t *testing.T) {
|
|||
request := NewFirstRequest(fd.Topic)
|
||||
request.Epoch = lookup.GetNextEpoch(epoch, T)
|
||||
request.data = generateData(T) // this generates some data that depends on T, so we can check later
|
||||
request.Sign(signer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := request.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
_, err = rh.Update(ctx, request)
|
||||
t.Ok(err)
|
||||
|
||||
if _, err := rh.Update(ctx, request); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
epoch = request.Epoch
|
||||
lastUpdateTime = T
|
||||
}
|
||||
|
||||
query := NewQuery(&fd, today, lookup.NoClue)
|
||||
|
||||
_, err = rh.Lookup(ctx, query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err := rh.Lookup(ctx, query)
|
||||
t.Ok(err)
|
||||
|
||||
_, content, err := rh.GetContent(&fd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(generateData(lastUpdateTime), content) {
|
||||
t.Fatalf("Expected to recover last written value %d, got %s", lastUpdateTime, string(content))
|
||||
}
|
||||
t.Ok(err)
|
||||
t.Equals(generateData(lastUpdateTime), content)
|
||||
|
||||
// lookup the closest update to 35*Year + 6* Month (~ June 2005):
|
||||
// it should find the update we put on 35*Year, since we were updating every 5 years.
|
||||
|
|
@ -315,21 +262,16 @@ func TestSparseUpdates(t *testing.T) {
|
|||
query.TimeLimit = 35*Year + 6*Month
|
||||
|
||||
_, err = rh.Lookup(ctx, query)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
_, content, err = rh.GetContent(&fd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(generateData(35*Year), content) {
|
||||
t.Fatalf("Expected to recover %d, got %s", 35*Year, string(content))
|
||||
}
|
||||
t.Ok(err)
|
||||
t.Equals(generateData(35*Year), content)
|
||||
}
|
||||
|
||||
func TestValidator(t *testing.T) {
|
||||
func TestValidator(tx *testing.T) {
|
||||
t := testutil.BeginTest(tx, false) // set to true to generate results
|
||||
defer t.FinishTest()
|
||||
|
||||
// make fake timeProvider
|
||||
timeProvider := &fakeTimeProvider{
|
||||
|
|
@ -338,13 +280,8 @@ func TestValidator(t *testing.T) {
|
|||
|
||||
// signer containing private key. Alice will be the good girl
|
||||
signer := newAliceSigner()
|
||||
|
||||
// set up sim timeProvider
|
||||
rh, _, teardownTest, err := setupTest(timeProvider, signer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer teardownTest()
|
||||
rh := setupTest(t, timeProvider)
|
||||
|
||||
// create new feed
|
||||
topic, _ := NewTopic(subtopicName, nil)
|
||||
|
|
@ -357,33 +294,27 @@ func TestValidator(t *testing.T) {
|
|||
// chunk with address
|
||||
data := []byte("foo")
|
||||
mr.SetData(data)
|
||||
if err := mr.Sign(signer); err != nil {
|
||||
t.Fatalf("sign fail: %v", err)
|
||||
}
|
||||
err := mr.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
chunk, err := mr.toChunk()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !rh.Validate(chunk) {
|
||||
t.Fatal("Chunk validator fail on update chunk")
|
||||
}
|
||||
t.Ok(err)
|
||||
t.Assert(rh.Validate(chunk), "Chunk validator fail on update chunk")
|
||||
|
||||
address := chunk.Address()
|
||||
// mess with the address
|
||||
address[0] = 11
|
||||
address[15] = 99
|
||||
|
||||
if rh.Validate(storage.NewChunk(address, chunk.Data())) {
|
||||
t.Fatal("Expected Validate to fail with false chunk address")
|
||||
}
|
||||
t.Assert(!rh.Validate(storage.NewChunk(address, chunk.Data())), "Expected Validate to fail with false chunk address")
|
||||
}
|
||||
|
||||
// tests that the content address validator correctly checks the data
|
||||
// tests that feed update chunks are passed through content address validator
|
||||
// there is some redundancy in this test as it also tests content addressed chunks,
|
||||
// which should be evaluated as invalid chunks by this validator
|
||||
func TestValidatorInStore(t *testing.T) {
|
||||
func TestValidatorInStore(tx *testing.T) {
|
||||
t := testutil.BeginTest(tx, false) // set to true to generate results
|
||||
defer t.FinishTest()
|
||||
|
||||
// make fake timeProvider
|
||||
TimestampProvider = &fakeTimeProvider{
|
||||
|
|
@ -392,24 +323,16 @@ func TestValidatorInStore(t *testing.T) {
|
|||
|
||||
// signer containing private key
|
||||
signer := newAliceSigner()
|
||||
|
||||
// set up localstore
|
||||
datadir, err := ioutil.TempDir("", "storage-testfeedsvalidator")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(datadir)
|
||||
datadir := t.Services.NewTempDir()
|
||||
|
||||
handlerParams := storage.NewDefaultLocalStoreParams()
|
||||
handlerParams.Init(datadir)
|
||||
store, err := storage.NewLocalStore(handlerParams, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
// set up Swarm feeds handler and add is as a validator to the localstore
|
||||
fhParams := &HandlerParams{}
|
||||
fh := NewHandler(fhParams)
|
||||
fh := NewHandler(&HandlerParams{})
|
||||
store.Validators = append(store.Validators, fh)
|
||||
|
||||
// create content addressed chunks, one good, one faulty
|
||||
|
|
@ -439,55 +362,28 @@ func TestValidatorInStore(t *testing.T) {
|
|||
r.Update.ID = id
|
||||
r.data = data
|
||||
|
||||
r.Sign(signer)
|
||||
err = r.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
uglyChunk, err := r.toChunk()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
// put the chunks in the store and check their error status
|
||||
err = store.Put(context.Background(), goodChunk)
|
||||
if err == nil {
|
||||
t.Fatal("expected error on good content address chunk with feed update validator only, but got nil")
|
||||
}
|
||||
t.MustFail(err, "expected error on good content address chunk with feed update validator only, but got nil")
|
||||
|
||||
err = store.Put(context.Background(), badChunk)
|
||||
if err == nil {
|
||||
t.Fatal("expected error on bad content address chunk with feed update validator only, but got nil")
|
||||
}
|
||||
err = store.Put(context.Background(), uglyChunk)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error on feed update chunk with feed update validator only, but got: %s", err)
|
||||
}
|
||||
t.MustFail(err, "expected error on bad content address chunk with feed update validator only, but got nil")
|
||||
|
||||
err = store.Put(context.Background(), uglyChunk) // feed update chunk with feed update validator only
|
||||
t.Ok(err)
|
||||
}
|
||||
|
||||
// create rpc and feeds Handler
|
||||
func setupTest(timeProvider timestampProvider, signer Signer) (fh *TestHandler, datadir string, teardown func(), err error) {
|
||||
|
||||
var fsClean func()
|
||||
var rpcClean func()
|
||||
cleanF = func() {
|
||||
if fsClean != nil {
|
||||
fsClean()
|
||||
}
|
||||
if rpcClean != nil {
|
||||
rpcClean()
|
||||
}
|
||||
}
|
||||
|
||||
// temp datadir
|
||||
datadir, err = ioutil.TempDir("", "fh")
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
fsClean = func() {
|
||||
os.RemoveAll(datadir)
|
||||
}
|
||||
|
||||
func setupTest(t *testutil.SwarmTestTools, timeProvider timestampProvider) *TestHandler {
|
||||
TimestampProvider = timeProvider
|
||||
fhParams := &HandlerParams{}
|
||||
fh, err = NewTestHandler(datadir, fhParams)
|
||||
return fh, datadir, cleanF, err
|
||||
fh := NewTestHandler(t, "")
|
||||
return fh
|
||||
}
|
||||
|
||||
func newAliceSigner() *GenericSigner {
|
||||
|
|
|
|||
|
|
@ -17,38 +17,27 @@
|
|||
package feed
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
)
|
||||
|
||||
func areEqualJSON(s1, s2 string) (bool, error) {
|
||||
//credit for the trick: turtlemonvh https://gist.github.com/turtlemonvh/e4f7404e28387fadb8ad275a99596f67
|
||||
var o1 interface{}
|
||||
var o2 interface{}
|
||||
|
||||
err := json.Unmarshal([]byte(s1), &o1)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Error mashalling string 1 :: %s", err.Error())
|
||||
}
|
||||
err = json.Unmarshal([]byte(s2), &o2)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Error mashalling string 2 :: %s", err.Error())
|
||||
}
|
||||
|
||||
return reflect.DeepEqual(o1, o2), nil
|
||||
}
|
||||
|
||||
// TestEncodingDecodingUpdateRequests ensures that requests are serialized properly
|
||||
// while also checking cryptographically that only the owner of a feed can update it.
|
||||
func TestEncodingDecodingUpdateRequests(t *testing.T) {
|
||||
func TestEncodingDecodingUpdateRequests(tx *testing.T) {
|
||||
t := testutil.BeginTest(tx, false) // set to true to generate results
|
||||
defer t.FinishTest()
|
||||
|
||||
TimestampProvider = &fakeTimeProvider{
|
||||
currentTime: startTime.Time, // clock starts at t=4200
|
||||
}
|
||||
|
||||
charlie := newCharlieSigner() //Charlie
|
||||
bob := newBobSigner() //Bob
|
||||
|
|
@ -58,28 +47,12 @@ func TestEncodingDecodingUpdateRequests(t *testing.T) {
|
|||
firstRequest := NewFirstRequest(topic)
|
||||
firstRequest.User = charlie.Address()
|
||||
|
||||
// We now encode the create message to simulate we send it over the wire
|
||||
messageRawData, err := firstRequest.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("Error encoding first feed update request: %s", err)
|
||||
}
|
||||
t.TestJSONMarshaller("firstrequest.json", firstRequest)
|
||||
|
||||
// ... the message arrives and is decoded...
|
||||
var recoveredFirstRequest Request
|
||||
if err := recoveredFirstRequest.UnmarshalJSON(messageRawData); err != nil {
|
||||
t.Fatalf("Error decoding first feed update request: %s", err)
|
||||
}
|
||||
|
||||
// ... but verification should fail because it is not signed!
|
||||
if err := recoveredFirstRequest.Verify(); err == nil {
|
||||
t.Fatal("Expected Verify to fail since the message is not signed")
|
||||
}
|
||||
|
||||
// We now assume that the feed ypdate was created and propagated.
|
||||
|
||||
const expectedSignature = "0x7235b27a68372ddebcf78eba48543fa460864b0b0e99cb533fcd3664820e603312d29426dd00fb39628f5299480a69bf6e462838d78de49ce0704c754c9deb2601"
|
||||
const expectedJSON = `{"feed":{"topic":"0x6120676f6f6420746f706963206e616d65000000000000000000000000000000","user":"0x876a8936a7cd0b79ef0735ad0896c1afe278781c"},"epoch":{"time":1000,"level":1},"protocolVersion":0,"data":"0x5468697320686f75722773207570646174653a20537761726d2039392e3020686173206265656e2072656c656173656421"}`
|
||||
// but verification should fail because it is not signed!
|
||||
t.MustFail(firstRequest.Verify(), "Expected Verify to fail since the message is not signed")
|
||||
|
||||
// We now assume that the feed update was created and propagated.
|
||||
//Put together an unsigned update request that we will serialize to send it to the signer.
|
||||
data := []byte("This hour's update: Swarm 99.0 has been released!")
|
||||
request := &Request{
|
||||
|
|
@ -95,96 +68,74 @@ func TestEncodingDecodingUpdateRequests(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
messageRawData, err = request.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("Error encoding update request: %s", err)
|
||||
}
|
||||
|
||||
equalJSON, err := areEqualJSON(string(messageRawData), expectedJSON)
|
||||
if err != nil {
|
||||
t.Fatalf("Error decoding update request JSON: %s", err)
|
||||
}
|
||||
if !equalJSON {
|
||||
t.Fatalf("Received a different JSON message. Expected %s, got %s", expectedJSON, string(messageRawData))
|
||||
}
|
||||
messageRawData, err := request.MarshalJSON()
|
||||
t.Ok(err)
|
||||
t.JSONBytesEqualsFile("secondrequest.json", messageRawData)
|
||||
|
||||
// now the encoded message messageRawData is sent over the wire and arrives to the signer
|
||||
|
||||
//Attempt to extract an UpdateRequest out of the encoded message
|
||||
var recoveredRequest Request
|
||||
if err := recoveredRequest.UnmarshalJSON(messageRawData); err != nil {
|
||||
t.Fatalf("Error decoding update request: %s", err)
|
||||
}
|
||||
err = recoveredRequest.UnmarshalJSON(messageRawData)
|
||||
t.Ok(err)
|
||||
|
||||
//sign the request and see if it matches our predefined signature above.
|
||||
if err := recoveredRequest.Sign(charlie); err != nil {
|
||||
t.Fatalf("Error signing request: %s", err)
|
||||
}
|
||||
|
||||
compareByteSliceToExpectedHex(t, "signature", recoveredRequest.Signature[:], expectedSignature)
|
||||
err = recoveredRequest.Sign(charlie)
|
||||
t.Ok(err)
|
||||
t.EqualsKey("signature", hexutil.Encode(recoveredRequest.Signature[:]))
|
||||
|
||||
// mess with the signature and see what happens. To alter the signature, we briefly decode it as JSON
|
||||
// to alter the signature field.
|
||||
var j updateRequestJSON
|
||||
if err := json.Unmarshal([]byte(expectedJSON), &j); err != nil {
|
||||
t.Fatal("Error unmarshalling test json, check expectedJSON constant")
|
||||
}
|
||||
err = json.Unmarshal([]byte(messageRawData), &j)
|
||||
t.Ok(err)
|
||||
|
||||
j.Signature = "Certainly not a signature"
|
||||
corruptMessage, _ := json.Marshal(j) // encode the message with the bad signature
|
||||
var corruptRequest Request
|
||||
if err = corruptRequest.UnmarshalJSON(corruptMessage); err == nil {
|
||||
t.Fatal("Expected DecodeUpdateRequest to fail when trying to interpret a corrupt message with an invalid signature")
|
||||
}
|
||||
err = corruptRequest.UnmarshalJSON(corruptMessage)
|
||||
t.MustFail(err, "Expected DecodeUpdateRequest to fail when trying to interpret a corrupt message with an invalid signature")
|
||||
|
||||
// Now imagine Bob wants to create an update of his own about the same feed,
|
||||
// signing a message with his private key
|
||||
if err := request.Sign(bob); err != nil {
|
||||
t.Fatalf("Error signing: %s", err)
|
||||
}
|
||||
err = request.Sign(bob)
|
||||
t.Ok(err)
|
||||
|
||||
// Now Bob encodes the message to send it over the wire...
|
||||
messageRawData, err = request.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("Error encoding message:%s", err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
// ... the message arrives to our Swarm node and it is decoded.
|
||||
recoveredRequest = Request{}
|
||||
if err := recoveredRequest.UnmarshalJSON(messageRawData); err != nil {
|
||||
t.Fatalf("Error decoding message:%s", err)
|
||||
}
|
||||
err = recoveredRequest.UnmarshalJSON(messageRawData)
|
||||
t.Ok(err)
|
||||
|
||||
// Before checking what happened with Bob's update, let's see what would happen if we mess
|
||||
// with the signature big time to see if Verify catches it
|
||||
savedSignature := *recoveredRequest.Signature // save the signature for later
|
||||
binary.LittleEndian.PutUint64(recoveredRequest.Signature[5:], 556845463424) // write some random data to break the signature
|
||||
if err = recoveredRequest.Verify(); err == nil {
|
||||
t.Fatal("Expected Verify to fail on corrupt signature")
|
||||
}
|
||||
err = recoveredRequest.Verify()
|
||||
t.MustFail(err, "Expected Verify to fail on corrupt signature")
|
||||
|
||||
// restore the Bob's signature from corruption
|
||||
*recoveredRequest.Signature = savedSignature
|
||||
|
||||
// Now the signature is not corrupt
|
||||
if err = recoveredRequest.Verify(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = recoveredRequest.Verify()
|
||||
t.Ok(err)
|
||||
|
||||
// Reuse object and sign with our friend Charlie's private key
|
||||
if err := recoveredRequest.Sign(charlie); err != nil {
|
||||
t.Fatalf("Error signing with the correct private key: %s", err)
|
||||
}
|
||||
err = recoveredRequest.Sign(charlie)
|
||||
t.Ok(err)
|
||||
|
||||
// And now, Verify should work since this update now belongs to Charlie
|
||||
if err = recoveredRequest.Verify(); err != nil {
|
||||
t.Fatalf("Error verifying that Charlie, can sign a reused request object:%s", err)
|
||||
}
|
||||
err = recoveredRequest.Verify()
|
||||
t.Ok(err)
|
||||
|
||||
// mess with the lookup key to make sure Verify fails:
|
||||
recoveredRequest.Time = 77999 // this will alter the lookup key
|
||||
if err = recoveredRequest.Verify(); err == nil {
|
||||
t.Fatalf("Expected Verify to fail since the lookup key has been altered")
|
||||
}
|
||||
err = recoveredRequest.Verify()
|
||||
t.MustFail(err, "Expected Verify to fail since the lookup key has been altered")
|
||||
}
|
||||
|
||||
func getTestRequest() *Request {
|
||||
|
|
@ -233,28 +184,18 @@ func TestUpdateChunkSerializationErrorChecking(t *testing.T) {
|
|||
}
|
||||
|
||||
// check that signature address matches update signer address
|
||||
func TestReverse(t *testing.T) {
|
||||
func TestReverse(tx *testing.T) {
|
||||
t := testutil.BeginTest(tx, false) // set to true to generate results
|
||||
defer t.FinishTest()
|
||||
|
||||
epoch := lookup.Epoch{
|
||||
Time: 7888,
|
||||
Level: 6,
|
||||
}
|
||||
|
||||
// make fake timeProvider
|
||||
timeProvider := &fakeTimeProvider{
|
||||
currentTime: startTime.Time,
|
||||
}
|
||||
|
||||
// signer containing private key
|
||||
signer := newAliceSigner()
|
||||
|
||||
// set up rpc and create feeds handler
|
||||
_, _, teardownTest, err := setupTest(timeProvider, signer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer teardownTest()
|
||||
|
||||
topic, _ := NewTopic("Cervantes quotes", nil)
|
||||
fd := Feed{
|
||||
Topic: topic,
|
||||
|
|
@ -271,42 +212,30 @@ func TestReverse(t *testing.T) {
|
|||
// generate a chunk key for this request
|
||||
key := request.Addr()
|
||||
|
||||
if err = request.Sign(signer); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := request.Sign(signer)
|
||||
t.Ok(err)
|
||||
|
||||
chunk, err := request.toChunk()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
// check that we can recover the owner account from the update chunk's signature
|
||||
var checkUpdate Request
|
||||
if err := checkUpdate.fromChunk(chunk); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = checkUpdate.fromChunk(chunk)
|
||||
t.Ok(err)
|
||||
|
||||
checkdigest, err := checkUpdate.GetDigest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
recoveredAddr, err := getUserAddr(checkdigest, *checkUpdate.Signature)
|
||||
if err != nil {
|
||||
t.Fatalf("Retrieve address from signature fail: %v", err)
|
||||
}
|
||||
t.Ok(err)
|
||||
|
||||
recoveredAddr, err := getUserAddr(checkdigest, *checkUpdate.Signature) //Retrieve address from signature
|
||||
t.Ok(err)
|
||||
|
||||
originalAddr := crypto.PubkeyToAddress(signer.PrivKey.PublicKey)
|
||||
|
||||
// check that the metadata retrieved from the chunk matches what we gave it
|
||||
if recoveredAddr != originalAddr {
|
||||
t.Fatalf("addresses dont match: %x != %x", originalAddr, recoveredAddr)
|
||||
}
|
||||
t.Assert(recoveredAddr == originalAddr, "addresses dont match: %x != %x", originalAddr, recoveredAddr)
|
||||
t.Equals(chunk.Address()[:], key[:])
|
||||
|
||||
if !bytes.Equal(key[:], chunk.Address()[:]) {
|
||||
t.Fatalf("Expected chunk key '%x', was '%x'", key, chunk.Address())
|
||||
}
|
||||
if epoch != checkUpdate.Epoch {
|
||||
t.Fatalf("Expected epoch to be '%s', was '%s'", epoch.String(), checkUpdate.Epoch.String())
|
||||
}
|
||||
if !bytes.Equal(data, checkUpdate.data) {
|
||||
t.Fatalf("Expected data '%x', was '%x'", data, checkUpdate.data)
|
||||
}
|
||||
t.Assert(epoch == checkUpdate.Epoch, "Expected epoch to be '%s', was '%s'", epoch.String(), checkUpdate.Epoch.String())
|
||||
|
||||
t.Equals(checkUpdate.data, data)
|
||||
}
|
||||
|
|
|
|||
11
swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/firstrequest.json
vendored
Normal file
11
swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/firstrequest.json
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"feed": {
|
||||
"topic": "0x6120676f6f6420746f706963206e616d65000000000000000000000000000000",
|
||||
"user": "0x876a8936a7cd0b79ef0735ad0896c1afe278781c"
|
||||
},
|
||||
"epoch": {
|
||||
"time": 4200,
|
||||
"level": 25
|
||||
},
|
||||
"protocolVersion": 0
|
||||
}
|
||||
3
swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/results.json
vendored
Normal file
3
swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/results.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"signature": "0x7235b27a68372ddebcf78eba48543fa460864b0b0e99cb533fcd3664820e603312d29426dd00fb39628f5299480a69bf6e462838d78de49ce0704c754c9deb2601"
|
||||
}
|
||||
12
swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/secondrequest.json
vendored
Normal file
12
swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/secondrequest.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"feed": {
|
||||
"topic": "0x6120676f6f6420746f706963206e616d65000000000000000000000000000000",
|
||||
"user": "0x876a8936a7cd0b79ef0735ad0896c1afe278781c"
|
||||
},
|
||||
"epoch": {
|
||||
"time": 1000,
|
||||
"level": 1
|
||||
},
|
||||
"protocolVersion": 0,
|
||||
"data": "0x5468697320686f75722773207570646174653a20537761726d2039392e3020686173206265656e2072656c656173656421"
|
||||
}
|
||||
3
swarm/storage/feed/testdata/TestTopic/results.json
vendored
Normal file
3
swarm/storage/feed/testdata/TestTopic/results.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"hex": "0xdfa89c750e3108f9c2aeef0123456789abcdef0123456789abcdef0123456789"
|
||||
}
|
||||
1
swarm/storage/feed/testdata/TestTopic/topic.json
vendored
Normal file
1
swarm/storage/feed/testdata/TestTopic/topic.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
"0xdfa89c750e3108f9c2aeef0123456789abcdef0123456789abcdef0123456789"
|
||||
|
|
@ -18,20 +18,16 @@ package feed
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
testDbDirName = "feeds"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
)
|
||||
|
||||
type TestHandler struct {
|
||||
*Handler
|
||||
dataDir string
|
||||
}
|
||||
|
||||
func (t *TestHandler) Close() {
|
||||
|
|
@ -50,22 +46,24 @@ func newFakeNetFetcher(context.Context, storage.Address, *sync.Map) storage.NetF
|
|||
}
|
||||
|
||||
// NewTestHandler creates Handler object to be used for testing purposes.
|
||||
func NewTestHandler(datadir string, params *HandlerParams) (*TestHandler, error) {
|
||||
path := filepath.Join(datadir, testDbDirName)
|
||||
fh := NewHandler(params)
|
||||
func NewTestHandler(t *testutil.SwarmTestTools, path string) *TestHandler {
|
||||
if path == "" {
|
||||
path = t.Services.NewTempDir()
|
||||
}
|
||||
fh := NewHandler(&HandlerParams{})
|
||||
localstoreparams := storage.NewDefaultLocalStoreParams()
|
||||
localstoreparams.Init(path)
|
||||
localStore, err := storage.NewLocalStore(localstoreparams, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("localstore create fail, path %s: %v", path, err)
|
||||
}
|
||||
t.Ok(err)
|
||||
localStore.Validators = append(localStore.Validators, storage.NewContentAddressValidator(storage.MakeHashFunc(feedsHashAlgorithm)))
|
||||
localStore.Validators = append(localStore.Validators, fh)
|
||||
netStore, err := storage.NewNetStore(localStore, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.Ok(err)
|
||||
netStore.NewNetFetcherFunc = newFakeNetFetcher
|
||||
fh.SetStore(netStore)
|
||||
return &TestHandler{fh}, nil
|
||||
th := &TestHandler{
|
||||
Handler: fh,
|
||||
dataDir: path,
|
||||
}
|
||||
return th
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,47 +4,23 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
)
|
||||
|
||||
func TestTopic(t *testing.T) {
|
||||
func TestTopic(tx *testing.T) {
|
||||
t := testutil.BeginTest(tx, false) // set to true to generate results
|
||||
defer t.FinishTest()
|
||||
|
||||
related, _ := hexutil.Decode("0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789")
|
||||
topicName := "test-topic"
|
||||
topic, _ := NewTopic(topicName, related)
|
||||
hex := topic.Hex()
|
||||
expectedHex := "0xdfa89c750e3108f9c2aeef0123456789abcdef0123456789abcdef0123456789"
|
||||
if hex != expectedHex {
|
||||
t.Fatalf("Expected %s, got %s", expectedHex, hex)
|
||||
}
|
||||
t.EqualsKey("hex", hex)
|
||||
|
||||
var topic2 Topic
|
||||
topic2.FromHex(hex)
|
||||
if topic2 != topic {
|
||||
t.Fatal("Expected recovered topic to be equal to original one")
|
||||
}
|
||||
|
||||
if topic2.Name(related) != topicName {
|
||||
t.Fatal("Retrieved name does not match")
|
||||
}
|
||||
|
||||
bytes, err := topic2.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expectedJSON := `"0xdfa89c750e3108f9c2aeef0123456789abcdef0123456789abcdef0123456789"`
|
||||
equal, err := areEqualJSON(expectedJSON, string(bytes))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !equal {
|
||||
t.Fatalf("Expected JSON to be %s, got %s", expectedJSON, string(bytes))
|
||||
}
|
||||
|
||||
err = topic2.UnmarshalJSON(bytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if topic2 != topic {
|
||||
t.Fatal("Expected recovered topic to be equal to original one")
|
||||
}
|
||||
t.Equals(topic, topic2)
|
||||
t.Equals(topicName, topic2.Name(related))
|
||||
|
||||
t.TestJSONMarshaller("topic.json", &topic2)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
//TODO: refactor these as TestServices
|
||||
|
||||
// TempFileWithContent is a helper function that creates a temp file that contains the following string content then closes the file handle
|
||||
// it returns the complete file path
|
||||
func TempFileWithContent(t *testing.T, content string) string {
|
||||
|
|
|
|||
20
swarm/testutil/swarmtestservices.go
Normal file
20
swarm/testutil/swarmtestservices.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package testutil
|
||||
|
||||
import (
|
||||
"github.com/epiclabs-io/ut"
|
||||
)
|
||||
|
||||
// SwarmTestServices groups all third-party dependencies to provide easy instantiation
|
||||
// and cleanup of these. By default, it provides temporary files and directories
|
||||
type swarmTestServices struct {
|
||||
tt *SwarmTestTools
|
||||
*ut.FileServices
|
||||
}
|
||||
|
||||
// Instantiate Test services
|
||||
func newSwarmTestServices(tt *SwarmTestTools) *swarmTestServices {
|
||||
return &swarmTestServices{
|
||||
tt: tt,
|
||||
FileServices: ut.NewFileServices(tt.TestTools),
|
||||
}
|
||||
}
|
||||
21
swarm/testutil/testtools.go
Normal file
21
swarm/testutil/testtools.go
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package testutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/epiclabs-io/ut"
|
||||
)
|
||||
|
||||
// SwarmTestTools are Swarm-specific test tools
|
||||
type SwarmTestTools struct {
|
||||
*ut.TestTools
|
||||
Services *swarmTestServices
|
||||
}
|
||||
|
||||
// BeginTest returns a project-specific test toolbox
|
||||
func BeginTest(tb testing.TB, generateResults bool) *SwarmTestTools {
|
||||
ett := new(SwarmTestTools)
|
||||
ett.TestTools = ut.BeginTest(tb, generateResults)
|
||||
ett.Services = newSwarmTestServices(ett)
|
||||
return ett
|
||||
}
|
||||
Loading…
Reference in a new issue