swarm/, cmd/swarm: Amend comments from @lmars PR 204

This commit is contained in:
lash 2018-01-21 03:13:52 +01:00
parent 249e1a8aaa
commit a16d0af41d
11 changed files with 166 additions and 217 deletions

View file

@ -560,7 +560,7 @@ func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.
} }
// In production, mockStore must be always nil. // In production, mockStore must be always nil.
return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, bzzconfig.SwapEnabled, bzzconfig.SyncEnabled, bzzconfig.Cors, bzzconfig.PssEnabled, bzzconfig.ResourceEnabled, nil) return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, nil)
} }
//register within the ethereum node //register within the ethereum node
if err := stack.Register(boot); err != nil { if err := stack.Register(boot); err != nil {

View file

@ -23,7 +23,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"strconv"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -77,13 +76,19 @@ type rpcBlock struct {
UncleHashes []common.Hash `json:"uncles"` UncleHashes []common.Hash `json:"uncles"`
} }
func (ec *Client) BlockNumber(ctx context.Context) (uint64, error) { func (ec *Client) BlockNumber(ctx context.Context) (big.Int, error) {
var number string var numberstr string
err := ec.c.CallContext(ctx, &number, "eth_blockNumber") number := &big.Int{}
err := ec.c.CallContext(ctx, &numberstr, "eth_blockNumber")
if err != nil { if err != nil {
return 0, err return *number, err
} }
return strconv.ParseUint(number, 10, 64) var ok bool
number, ok = number.SetString(numberstr, 10)
if !ok {
err = errors.New("Failed to parse bigint")
}
return *number, err
} }
func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) { func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) {

View file

@ -365,13 +365,13 @@ func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storag
} }
// Look up mutable resource updates at specific periods and versions // Look up mutable resource updates at specific periods and versions
func (self *Api) DbLookup(key storage.Key, name string, period uint32, version uint32) (storage.Key, io.ReadSeeker, int, error) { func (self *Api) DbLookup(key storage.Key, name string, period uint32, version uint32) (storage.Key, []byte, error) {
var err error var err error
if version != 0 { if version != 0 {
if period == 0 { if period == 0 {
currentblocknumber, err := self.resource.GetBlock() currentblocknumber, err := self.resource.GetBlock()
if err != nil { if err != nil {
return nil, nil, 0, fmt.Errorf("Could not determine latest block: %v", err) return nil, nil, fmt.Errorf("Could not determine latest block: %v", err)
} }
period = self.resource.BlockToPeriod(name, currentblocknumber) period = self.resource.BlockToPeriod(name, currentblocknumber)
} }
@ -382,13 +382,13 @@ func (self *Api) DbLookup(key storage.Key, name string, period uint32, version u
_, err = self.resource.LookupLatestByName(name, true) _, err = self.resource.LookupLatestByName(name, true)
} }
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, err
} }
key, data, err := self.resource.GetContent(name) key, data, err := self.resource.GetContent(name)
if err != nil { if err != nil {
return nil, nil, 0, err return nil, nil, err
} }
return key, bytes.NewReader(data), len(data), nil return key, data, nil
} }
func (self *Api) DbCreate(name string, frequency uint64) (err error) { func (self *Api) DbCreate(name string, frequency uint64) (err error) {

View file

@ -110,8 +110,8 @@ func (self *Config) Init(prvKey *ecdsa.PrivateKey) {
self.PublicKey = pubkeyhex self.PublicKey = pubkeyhex
self.BzzKey = keyhex self.BzzKey = keyhex
self.Swap.Init(self.Contract, prvKey) if self.SwapEnabled {
//self.SyncParams.Init(self.Path) self.Swap.Init(self.Contract, prvKey)
//self.HiveParams.Init(self.Path) }
self.StoreParams.Init(self.Path) self.StoreParams.Init(self.Path)
} }

View file

@ -49,12 +49,9 @@ func TestConfig(t *testing.T) {
if one.PublicKey == "" { if one.PublicKey == "" {
t.Fatal("Expected PublicKey to be set") t.Fatal("Expected PublicKey to be set")
} }
if one.Swap.PayProfile.Beneficiary == (common.Address{}) && one.SwapEnabled {
//the Init function should append subdirs to the given path
if one.Swap.PayProfile.Beneficiary == (common.Address{}) {
t.Fatal("Failed to correctly initialize SwapParams") t.Fatal("Failed to correctly initialize SwapParams")
} }
if one.StoreParams.ChunkDbPath == one.Path { if one.StoreParams.ChunkDbPath == one.Path {
t.Fatal("Failed to correctly initialize StoreParams") t.Fatal("Failed to correctly initialize StoreParams")
} }

View file

@ -295,13 +295,12 @@ func (s *Server) HandlePostDb(w http.ResponseWriter, r *Request) {
if r.ContentLength == 0 { if r.ContentLength == 0 {
frequency, err := strconv.ParseUint(r.uri.Path, 10, 64) frequency, err := strconv.ParseUint(r.uri.Path, 10, 64)
if err != nil { if err != nil {
w.WriteHeader(http.StatusBadRequest) s.BadRequest(w, r, fmt.Sprintf("Cannot parse frequency parameter: %v", err))
http.ServeContent(w, &r.Request, "", time.Now(), bytes.NewReader([]byte(err.Error())))
return return
} }
err = s.api.DbCreate(r.uri.Addr, frequency) err = s.api.DbCreate(r.uri.Addr, frequency)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) s.Error(w, r, fmt.Errorf("Resource creation failed: %v", err))
return return
} }
} else { } else {
@ -324,8 +323,8 @@ func (s *Server) HandlePostDb(w http.ResponseWriter, r *Request) {
// bzz-db[-[immutable|-raw]]://<id> - get latest update // bzz-db[-[immutable|-raw]]://<id> - get latest update
// bzz-db[-[immutable|-raw]]://<id>/<n> - get latest update on period n // bzz-db[-[immutable|-raw]]://<id>/<n> - get latest update on period n
// bzz-db[-[immutable|-raw]]://<id>/<n>/<m> - get update version m of period n // bzz-db[-[immutable|-raw]]://<id>/<n>/<m> - get update version m of period n
// <id> = ens name or hash
func (s *Server) HandleGetDb(w http.ResponseWriter, r *Request) { func (s *Server) HandleGetDb(w http.ResponseWriter, r *Request) {
w.Header().Set("Content-Type", "application/octet-stream")
rootKey, err := s.api.Resolve(r.uri) rootKey, err := s.api.Resolve(r.uri)
if err != nil { if err != nil {
@ -340,34 +339,39 @@ func (s *Server) HandleGetDb(w http.ResponseWriter, r *Request) {
var updateKey storage.Key var updateKey storage.Key
var period uint64 var period uint64
var version uint64 var version uint64
var data io.ReadSeeker var data []byte
var dataLength int var dataLength int
now := time.Now() now := time.Now()
switch len(params) { switch len(params) {
case 0: case 0:
updateKey, data, dataLength, err = s.api.DbLookup(rootKey, r.uri.Addr, 0, 0) updateKey, data, err = s.api.DbLookup(rootKey, r.uri.Addr, 0, 0)
break
case 2: case 2:
version, err = strconv.ParseUint(params[1], 10, 32) version, err = strconv.ParseUint(params[1], 10, 32)
if err != nil { if err != nil {
break break
} }
updateKey, data, err = s.api.DbLookup(rootKey, r.uri.Addr, uint32(period), uint32(version))
case 1: case 1:
version, err = strconv.ParseUint(params[1], 10, 32)
if err != nil {
break
}
period, err = strconv.ParseUint(params[0], 10, 32) period, err = strconv.ParseUint(params[0], 10, 32)
if err != nil { if err != nil {
break break
} }
updateKey, data, dataLength, err = s.api.DbLookup(rootKey, r.uri.Addr, uint32(period), uint32(version)) updateKey, data, err = s.api.DbLookup(rootKey, r.uri.Addr, uint32(period), uint32(version))
break
default: default:
w.WriteHeader(http.StatusBadRequest) s.BadRequest(w, r, fmt.Sprintf("Invalid mutable resource request"))
err = fmt.Errorf("params 0-2") return
} }
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) s.Error(w, r, fmt.Errorf("Mutable resource lookup failed: %v", err))
return return
} }
if !r.uri.DbRaw() { if !r.uri.DbRaw() {
w.Header().Set("Content-Type", "application/octet-stream")
} else {
entry := api.ManifestEntry{ entry := api.ManifestEntry{
Hash: rootKey.Hex(), Hash: rootKey.Hex(),
Path: updateKey.Hex(), Path: updateKey.Hex(),
@ -376,9 +380,9 @@ func (s *Server) HandleGetDb(w http.ResponseWriter, r *Request) {
ModTime: now, ModTime: now,
Status: http.StatusOK, Status: http.StatusOK,
} }
mode := (6 << 2) | (4 << 1) | 4 mode := 0644
if s.api.DbIsValidated() { if s.api.DbIsValidated() {
mode |= 2 << 1 mode |= (2 << 3) | 2
} }
entry.Mode = int64(mode) entry.Mode = int64(mode)
manifest := api.Manifest{ manifest := api.Manifest{
@ -388,12 +392,13 @@ func (s *Server) HandleGetDb(w http.ResponseWriter, r *Request) {
} }
manifestJson, err := json.Marshal(manifest) manifestJson, err := json.Marshal(manifest)
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) s.Error(w, r, fmt.Errorf("Could not convert manifest to json: %v", err))
return return
} }
data = bytes.NewReader(manifestJson) w.Header().Set("Content-Type", api.DbManifestType)
data = []byte(manifestJson)
} }
http.ServeContent(w, &r.Request, "", now, data) http.ServeContent(w, &r.Request, "", now, bytes.NewReader(data))
} }
// HandleGet handles a GET request to // HandleGet handles a GET request to

View file

@ -47,46 +47,53 @@ func TestBzzGetDb(t *testing.T) {
keybytes := make([]byte, common.HashLength) // nearest we get to source of info keybytes := make([]byte, common.HashLength) // nearest we get to source of info
_, err := rand.Read(keybytes) _, err := rand.Read(keybytes)
if err != nil { if err != nil {
fmt.Printf("err: %v\n", err) t.Fatal(err)
return
} }
url := fmt.Sprintf("%s/bzz-db:/%s/42", srv.URL, fmt.Sprintf("%x", keybytes)) url := fmt.Sprintf("%s/bzz-db:/%s/42", srv.URL, fmt.Sprintf("%x", keybytes))
resp, err := http.Post(url, "application/octet-stream", nil) resp, err := http.Post(url, "application/octet-stream", nil)
if err != nil { if err != nil {
fmt.Printf("err: %v\n", err) t.Fatal(err)
return
} }
b, err := ioutil.ReadAll(resp.Body) b, err := ioutil.ReadAll(resp.Body)
fmt.Printf("Create: %s : %s\n", resp.Status, b) if err != nil {
t.Fatal(err)
}
log.Debug("Create", "status", resp.Status, "body", b)
url = fmt.Sprintf("%s/bzz-db:/%s", srv.URL, fmt.Sprintf("%x", keybytes)) url = fmt.Sprintf("%s/bzz-db:/%x", srv.URL, keybytes)
data := []byte("foo") data := []byte("foo")
resp, err = http.Post(url, "application/octet-stream", bytes.NewReader(data)) resp, err = http.Post(url, "application/octet-stream", bytes.NewReader(data))
if err != nil { if err != nil {
fmt.Printf("err: %v\n", err) t.Fatal(err)
return
} }
b, err = ioutil.ReadAll(resp.Body) b, err = ioutil.ReadAll(resp.Body)
fmt.Printf("Update: %s : %s\n", resp.Status, b) if err != nil {
t.Fatal(err)
}
log.Debug("Update", "status", resp.Status, "body", b)
url = fmt.Sprintf("%s/bzz-db-raw:/%s", srv.URL, fmt.Sprintf("%x", keybytes)) url = fmt.Sprintf("%s/bzz-db-raw:/%s", srv.URL, fmt.Sprintf("%x", keybytes))
resp, err = http.Get(url) resp, err = http.Get(url)
if err != nil { if err != nil {
fmt.Printf("err: %v\n", err) t.Fatal(err)
return
} }
b, err = ioutil.ReadAll(resp.Body) b, err = ioutil.ReadAll(resp.Body)
fmt.Printf("Get: %s : %s\n", resp.Status, b) if err != nil {
t.Fatal(err)
}
log.Debug("Get raw", "status", resp.Status, "body", b)
url = fmt.Sprintf("%s/bzz-db:/%s", srv.URL, fmt.Sprintf("%x", keybytes)) url = fmt.Sprintf("%s/bzz-db:/%s", srv.URL, fmt.Sprintf("%x", keybytes))
resp, err = http.Get(url) resp, err = http.Get(url)
if err != nil { if err != nil {
fmt.Printf("err: %v\n", err) t.Fatal(err)
return
} }
b, err = ioutil.ReadAll(resp.Body) b, err = ioutil.ReadAll(resp.Body)
fmt.Printf("Get: %s : %s\n", resp.Status, b) if err != nil {
t.Fatal(err)
}
log.Debug("Get manifest", "status", resp.Status, "body", b)
} }

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"math/big"
"path/filepath" "path/filepath"
"sync" "sync"
"time" "time"
@ -12,7 +13,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
@ -58,6 +58,10 @@ type ResourceValidator interface {
sign(common.Hash) (Signature, error) // SignFunc sign(common.Hash) (Signature, error) // SignFunc
} }
type ethApi interface {
BlockNumber(context.Context) (big.Int, error)
}
// Mutable resource is an entity which allows updates to a resource // Mutable resource is an entity which allows updates to a resource
// without resorting to ENS on each update. // without resorting to ENS on each update.
// The update scheme is built on swarm chunks with chunk keys following // The update scheme is built on swarm chunks with chunk keys following
@ -119,8 +123,10 @@ type ResourceValidator interface {
// TODO: Include modtime in chunk data + signature // TODO: Include modtime in chunk data + signature
type ResourceHandler struct { type ResourceHandler struct {
ChunkStore ChunkStore
ctx context.Context
cancelFunc func()
validator ResourceValidator validator ResourceValidator
ethClient *ethclient.Client ethClient ethApi
resources map[string]*resource resources map[string]*resource
hashLock sync.Mutex hashLock sync.Mutex
resourceLock sync.RWMutex resourceLock sync.RWMutex
@ -134,7 +140,7 @@ type ResourceHandler struct {
// Create or open resource update chunk store // Create or open resource update chunk store
// //
// If validator is nil, signature and access validation will be deactivated // If validator is nil, signature and access validation will be deactivated
func NewResourceHandler(datadir string, cloudStore CloudStore, ethClient *ethclient.Client, validator ResourceValidator) (*ResourceHandler, error) { func NewResourceHandler(datadir string, cloudStore CloudStore, ethClient ethApi, validator ResourceValidator) (*ResourceHandler, error) {
hashfunc := MakeHashFunc(SHA3Hash) hashfunc := MakeHashFunc(SHA3Hash)
@ -602,17 +608,11 @@ func (self *ResourceHandler) Close() {
} }
func (self *ResourceHandler) GetBlock() (uint64, error) { func (self *ResourceHandler) GetBlock() (uint64, error) {
return self.ethClient.BlockNumber(self.ctx) bigblocknumber, err := self.ethClient.BlockNumber(self.ctx)
// get the block height and convert to uint64 if err != nil {
// var currentblock string return 0, err
// err := self.rpcClient.Call(&currentblock, "eth_blockNumber") }
// if err != nil { return bigblocknumber.Uint64(), nil
// return 0, err
// }
// if currentblock == "0x0" {
// return 0, nil
// }
// return strconv.ParseUint(currentblock, 10, 64)
} }
// Calculate the period index (aka major version number) from a given block number // Calculate the period index (aka major version number) from a given block number
@ -776,10 +776,7 @@ func isSafeName(name string) bool {
if err != nil { if err != nil {
return false return false
} }
if validname != name { return validname == name
return false
}
return true
} }
// convenience for creating signature hashes of update data // convenience for creating signature hashes of update data

View file

@ -2,6 +2,7 @@ package storage
import ( import (
"bytes" "bytes"
"context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
@ -9,8 +10,6 @@ import (
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"os" "os"
"path/filepath"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -22,9 +21,7 @@ import (
"github.com/ethereum/go-ethereum/contracts/ens/contract" "github.com/ethereum/go-ethereum/contracts/ens/contract"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc"
) )
var ( var (
@ -50,7 +47,7 @@ func init() {
// so we use this wrapper to fake returning the block count // so we use this wrapper to fake returning the block count
type fakeBackend struct { type fakeBackend struct {
*backends.SimulatedBackend *backends.SimulatedBackend
blocknumber uint64 blocknumber int64
} }
func (f *fakeBackend) Commit() { func (f *fakeBackend) Commit() {
@ -60,13 +57,10 @@ func (f *fakeBackend) Commit() {
f.blocknumber++ f.blocknumber++
} }
// for faking the rpc service, since we don't need the whole node stack func (f *fakeBackend) BlockNumber(context context.Context) (big.Int, error) {
type FakeRPC struct { f.blocknumber++
backend *fakeBackend biggie := big.NewInt(f.blocknumber)
} return *biggie, nil
func (r *FakeRPC) BlockNumber() (string, error) {
return strconv.FormatUint(r.backend.blocknumber, 10), nil
} }
// check that signature address matches update signer address // check that signature address matches update signer address
@ -84,8 +78,9 @@ func TestResourceReverse(t *testing.T) {
// set up rpc and create resourcehandler // set up rpc and create resourcehandler
rh, _, _, teardownTest, err := setupTest(nil, newTestValidator(signer.signContent)) rh, _, _, teardownTest, err := setupTest(nil, newTestValidator(signer.signContent))
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
defer teardownTest()
// generate a hash for block 4200 version 1 // generate a hash for block 4200 version 1
key := rh.resourceHash(period, version, rh.nameHash(safeName)) key := rh.resourceHash(period, version, rh.nameHash(safeName))
@ -94,14 +89,14 @@ func TestResourceReverse(t *testing.T) {
data := make([]byte, 8) data := make([]byte, 8)
_, err = rand.Read(data) _, err = rand.Read(data)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
testHasher.Reset() testHasher.Reset()
testHasher.Write(data) testHasher.Write(data)
digest := rh.keyDataHash(key, data) digest := rh.keyDataHash(key, data)
sig, err := rh.validator.sign(digest) sig, err := rh.validator.sign(digest)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
chunk := newUpdateChunk(key, &sig, period, version, safeName, data) chunk := newUpdateChunk(key, &sig, period, version, safeName, data)
@ -111,31 +106,30 @@ func TestResourceReverse(t *testing.T) {
checkdigest := rh.keyDataHash(chunk.Key, checkdata) checkdigest := rh.keyDataHash(chunk.Key, checkdata)
recoveredaddress, err := getAddressFromDataSig(checkdigest, *checksig) recoveredaddress, err := getAddressFromDataSig(checkdigest, *checksig)
if err != nil { if err != nil {
teardownTest(t, fmt.Errorf("Retrieve address from signature fail: %v", err)) t.Fatalf("Retrieve address from signature fail: %v", err)
} }
originaladdress := crypto.PubkeyToAddress(signer.privKey.PublicKey) originaladdress := crypto.PubkeyToAddress(signer.privKey.PublicKey)
// check that the metadata retrieved from the chunk matches what we gave it // check that the metadata retrieved from the chunk matches what we gave it
if recoveredaddress != originaladdress { if recoveredaddress != originaladdress {
teardownTest(t, fmt.Errorf("addresses dont match: %x != %x", originaladdress, recoveredaddress)) t.Fatalf("addresses dont match: %x != %x", originaladdress, recoveredaddress)
} }
if !bytes.Equal(key[:], chunk.Key[:]) { if !bytes.Equal(key[:], chunk.Key[:]) {
teardownTest(t, fmt.Errorf("Expected chunk key '%x', was '%x'", key, chunk.Key)) t.Fatalf("Expected chunk key '%x', was '%x'", key, chunk.Key)
} }
if period != checkperiod { if period != checkperiod {
teardownTest(t, fmt.Errorf("Expected period '%d', was '%d'", period, checkperiod)) t.Fatalf("Expected period '%d', was '%d'", period, checkperiod)
} }
if version != checkversion { if version != checkversion {
teardownTest(t, fmt.Errorf("Expected version '%d', was '%d'", version, checkversion)) t.Fatalf("Expected version '%d', was '%d'", version, checkversion)
} }
if safeName != checkname { if safeName != checkname {
teardownTest(t, fmt.Errorf("Expected name '%s', was '%s'", safeName, checkname)) t.Fatalf("Expected name '%s', was '%s'", safeName, checkname)
} }
if !bytes.Equal(data, checkdata) { if !bytes.Equal(data, checkdata) {
teardownTest(t, fmt.Errorf("Expectedn data '%x', was '%x'", data, checkdata)) t.Fatalf("Expectedn data '%x', was '%x'", data, checkdata)
} }
teardownTest(t, nil)
} }
// make updates and retrieve them based on periods and versions // make updates and retrieve them based on periods and versions
@ -143,34 +137,35 @@ func TestResourceHandler(t *testing.T) {
// make fake backend, set up rpc and create resourcehandler // make fake backend, set up rpc and create resourcehandler
backend := &fakeBackend{ backend := &fakeBackend{
blocknumber: startBlock, blocknumber: int64(startBlock),
} }
rh, datadir, _, teardownTest, err := setupTest(backend, nil) rh, datadir, _, teardownTest, err := setupTest(backend, nil)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
defer teardownTest()
// create a new resource // create a new resource
_, err = rh.NewResource(safeName, resourceFrequency) _, err = rh.NewResource(safeName, resourceFrequency)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
// check that the new resource is stored correctly // check that the new resource is stored correctly
namehash := rh.nameHash(safeName) namehash := rh.nameHash(safeName)
chunk, err := rh.ChunkStore.(*resourceChunkStore).localStore.(*LocalStore).memStore.Get(Key(namehash[:])) chunk, err := rh.ChunkStore.(*resourceChunkStore).localStore.(*LocalStore).memStore.Get(Key(namehash[:]))
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} else if len(chunk.SData) < 16 { } else if len(chunk.SData) < 16 {
teardownTest(t, fmt.Errorf("chunk data must be minimum 16 bytes, is %d", len(chunk.SData))) t.Fatalf("chunk data must be minimum 16 bytes, is %d", len(chunk.SData))
} }
startblocknumber := binary.LittleEndian.Uint64(chunk.SData[:8]) startblocknumber := binary.LittleEndian.Uint64(chunk.SData[:8])
chunkfrequency := binary.LittleEndian.Uint64(chunk.SData[8:]) chunkfrequency := binary.LittleEndian.Uint64(chunk.SData[8:])
if startblocknumber != backend.blocknumber { if startblocknumber != uint64(backend.blocknumber) {
teardownTest(t, fmt.Errorf("stored block number %d does not match provided block number %d", startblocknumber, backend.blocknumber)) t.Fatalf("stored block number %d does not match provided block number %d", startblocknumber, backend.blocknumber)
} }
if chunkfrequency != resourceFrequency { if chunkfrequency != resourceFrequency {
teardownTest(t, fmt.Errorf("stored frequency %d does not match provided frequency %d", chunkfrequency, resourceFrequency)) t.Fatalf("stored frequency %d does not match provided frequency %d", chunkfrequency, resourceFrequency)
} }
// update halfway to first period // update halfway to first period
@ -179,7 +174,7 @@ func TestResourceHandler(t *testing.T) {
data := []byte("blinky") data := []byte("blinky")
resourcekey["blinky"], err = rh.Update(safeName, data) resourcekey["blinky"], err = rh.Update(safeName, data)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
// update on first period // update on first period
@ -187,7 +182,7 @@ func TestResourceHandler(t *testing.T) {
data = []byte("pinky") data = []byte("pinky")
resourcekey["pinky"], err = rh.Update(safeName, data) resourcekey["pinky"], err = rh.Update(safeName, data)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
// update on second period // update on second period
@ -195,7 +190,7 @@ func TestResourceHandler(t *testing.T) {
data = []byte("inky") data = []byte("inky")
resourcekey["inky"], err = rh.Update(safeName, data) resourcekey["inky"], err = rh.Update(safeName, data)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
// update just after second period // update just after second period
@ -203,7 +198,7 @@ func TestResourceHandler(t *testing.T) {
data = []byte("clyde") data = []byte("clyde")
resourcekey["clyde"], err = rh.Update(safeName, data) resourcekey["clyde"], err = rh.Update(safeName, data)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
time.Sleep(time.Second) time.Sleep(time.Second)
rh.Close() rh.Close()
@ -215,43 +210,42 @@ func TestResourceHandler(t *testing.T) {
rh2, err := NewResourceHandler(datadir, &testCloudStore{}, rh.ethClient, nil) rh2, err := NewResourceHandler(datadir, &testCloudStore{}, rh.ethClient, nil)
_, err = rh2.LookupLatestByName(safeName, true) _, err = rh2.LookupLatestByName(safeName, true)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
// last update should be "clyde", version two, blockheight startblocknumber + (resourcefrequency * 3) // last update should be "clyde", version two, blockheight startblocknumber + (resourcefrequency * 3)
if !bytes.Equal(rh2.resources[safeName].data, []byte("clyde")) { if !bytes.Equal(rh2.resources[safeName].data, []byte("clyde")) {
teardownTest(t, fmt.Errorf("resource data was %v, expected %v", rh2.resources[safeName].data, []byte("clyde"))) t.Fatalf("resource data was %v, expected %v", rh2.resources[safeName].data, []byte("clyde"))
} }
if rh2.resources[safeName].version != 2 { if rh2.resources[safeName].version != 2 {
teardownTest(t, fmt.Errorf("resource version was %d, expected 2", rh2.resources[safeName].version)) t.Fatalf("resource version was %d, expected 2", rh2.resources[safeName].version)
} }
if rh2.resources[safeName].lastPeriod != 3 { if rh2.resources[safeName].lastPeriod != 3 {
teardownTest(t, fmt.Errorf("resource period was %d, expected 3", rh2.resources[safeName].lastPeriod)) t.Fatalf("resource period was %d, expected 3", rh2.resources[safeName].lastPeriod)
} }
log.Debug("Latest lookup", "period", rh2.resources[safeName].lastPeriod, "version", rh2.resources[safeName].version, "data", rh2.resources[safeName].data) log.Debug("Latest lookup", "period", rh2.resources[safeName].lastPeriod, "version", rh2.resources[safeName].version, "data", rh2.resources[safeName].data)
// specific block, latest version // specific block, latest version
rsrc, err := rh2.LookupHistoricalByName(safeName, 3, true) rsrc, err := rh2.LookupHistoricalByName(safeName, 3, true)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
// check data // check data
if !bytes.Equal(rsrc.data, []byte("clyde")) { if !bytes.Equal(rsrc.data, []byte("clyde")) {
teardownTest(t, fmt.Errorf("resource data (historical) was %v, expected %v", rh2.resources[domainName].data, []byte("clyde"))) t.Fatalf("resource data (historical) was %v, expected %v", rh2.resources[domainName].data, []byte("clyde"))
} }
log.Debug("Historical lookup", "period", rh2.resources[safeName].lastPeriod, "version", rh2.resources[safeName].version, "data", rh2.resources[safeName].data) log.Debug("Historical lookup", "period", rh2.resources[safeName].lastPeriod, "version", rh2.resources[safeName].version, "data", rh2.resources[safeName].data)
// specific block, specific version // specific block, specific version
rsrc, err = rh2.LookupVersionByName(safeName, 3, 1, true) rsrc, err = rh2.LookupVersionByName(safeName, 3, 1, true)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
// check data // check data
if !bytes.Equal(rsrc.data, []byte("inky")) { if !bytes.Equal(rsrc.data, []byte("inky")) {
teardownTest(t, fmt.Errorf("resource data (historical) was %v, expected %v", rh2.resources[domainName].data, []byte("inky"))) t.Fatalf("resource data (historical) was %v, expected %v", rh2.resources[domainName].data, []byte("inky"))
} }
log.Debug("Specific version lookup", "period", rh2.resources[safeName].lastPeriod, "version", rh2.resources[safeName].version, "data", rh2.resources[safeName].data) log.Debug("Specific version lookup", "period", rh2.resources[safeName].lastPeriod, "version", rh2.resources[safeName].version, "data", rh2.resources[safeName].data)
teardownTest(t, nil)
} }
@ -283,34 +277,33 @@ func TestResourceENSOwner(t *testing.T) {
// set up rpc and create resourcehandler with ENS sim backend // set up rpc and create resourcehandler with ENS sim backend
rh, _, _, teardownTest, err := setupTest(contractbackend, validator) rh, _, _, teardownTest, err := setupTest(contractbackend, validator)
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
defer teardownTest()
// create new resource when we are owner = ok // create new resource when we are owner = ok
_, err = rh.NewResource(safeName, resourceFrequency) _, err = rh.NewResource(safeName, resourceFrequency)
if err != nil { if err != nil {
teardownTest(t, fmt.Errorf("Create resource fail: %v", err)) t.Fatalf("Create resource fail: %v", err)
} }
data := []byte("foo") data := []byte("foo")
// update resource when we are owner = ok // update resource when we are owner = ok
_, err = rh.Update(safeName, data) _, err = rh.Update(safeName, data)
if err != nil { if err != nil {
teardownTest(t, fmt.Errorf("Update resource fail: %v", err)) t.Fatalf("Update resource fail: %v", err)
} }
// update resource when we are owner = ok // update resource when we are owner = ok
signertwo, err := newTestSigner() signertwo, err := newTestSigner()
if err != nil { if err != nil {
teardownTest(t, err) t.Fatal(err)
} }
rh.validator.(*ENSValidator).signFunc = signertwo.signContent rh.validator.(*ENSValidator).signFunc = signertwo.signContent
_, err = rh.Update(safeName, data) _, err = rh.Update(safeName, data)
if err == nil { if err == nil {
teardownTest(t, fmt.Errorf("Expected resource update fail due to owner mismatch")) t.Fatalf("Expected resource update fail due to owner mismatch")
} }
teardownTest(t, nil)
} }
// fast-forward blockheight // fast-forward blockheight
@ -321,7 +314,7 @@ func fwdBlocks(count int, backend *fakeBackend) {
} }
// create rpc and resourcehandler // create rpc and resourcehandler
func setupTest(contractbackend bind.ContractBackend, validator ResourceValidator) (rh *ResourceHandler, datadir string, signer *testSigner, teardown func(*testing.T, error), err error) { func setupTest(backend ethApi, validator ResourceValidator) (rh *ResourceHandler, datadir string, signer *testSigner, teardown func(), err error) {
var fsClean func() var fsClean func()
var rpcClean func() var rpcClean func()
@ -337,55 +330,18 @@ func setupTest(contractbackend bind.ContractBackend, validator ResourceValidator
// temp datadir // temp datadir
datadir, err = ioutil.TempDir("", "rh") datadir, err = ioutil.TempDir("", "rh")
if err != nil { if err != nil {
return return nil, "", nil, nil, err
} }
fsClean = func() { fsClean = func() {
os.RemoveAll(datadir) os.RemoveAll(datadir)
} }
// starting the whole stack just to get blocknumbers is too cumbersome rh, err = NewResourceHandler(datadir, &testCloudStore{}, backend, validator)
// so we fake the rpc server to get blocknumbers for testing return rh, datadir, signer, cleanF, nil
ipcpath := filepath.Join(datadir, "test.ipc")
ipcl, err := rpc.CreateIPCListener(ipcpath)
if err != nil {
return
}
rpcserver := rpc.NewServer()
var fake *fakeBackend
if contractbackend != nil {
fake = contractbackend.(*fakeBackend)
}
rpcserver.RegisterName("eth", &FakeRPC{
backend: fake,
})
go func() {
rpcserver.ServeListener(ipcl)
}()
rpcClean = func() {
rpcserver.Stop()
}
// connect to fake rpc
rpcClient, err := rpc.Dial(ipcpath)
if err != nil {
return
}
ethClient := ethclient.NewClient(rpcClient)
rh, err = NewResourceHandler(datadir, &testCloudStore{}, ethClient, validator)
teardown = func(t *testing.T, err error) {
cleanF()
if err != nil {
t.Fatal(err)
}
}
return
} }
// Set up simulated ENS backend for use with ENSResourceHandler tests // Set up simulated ENS backend for use with ENSResourceHandler tests
func setupENS(addr common.Address, transactOpts *bind.TransactOpts, sub string, top string) (common.Address, bind.ContractBackend, error) { func setupENS(addr common.Address, transactOpts *bind.TransactOpts, sub string, top string) (common.Address, *fakeBackend, error) {
// create the domain hash values to pass to the ENS contract methods // create the domain hash values to pass to the ENS contract methods
var tophash [32]byte var tophash [32]byte

View file

@ -54,15 +54,13 @@ type Swarm struct {
storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends storage storage.ChunkStore // internal access to storage, common interface to cloud storage backends
dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support dpa *storage.DPA // distributed preimage archive, the local API to the storage with document level storage/retrieval support
//depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage //depo network.StorageHandler // remote request handler, interface between bzz protocol and the storage
cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud) cloud storage.CloudStore // procurement, cloud storage backend (can multi-cloud)
bzz *network.Bzz // the logistic manager bzz *network.Bzz // the logistic manager
backend chequebook.Backend // simple blockchain Backend backend chequebook.Backend // simple blockchain Backend
privateKey *ecdsa.PrivateKey privateKey *ecdsa.PrivateKey
corsString string lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped
swapEnabled bool sfs *fuse.SwarmFS // need this to cleanup all the active mounts on node exit
lstore *storage.LocalStore // local store, needs to store for releasing resources after node stopped ps *pss.Pss
sfs *fuse.SwarmFS // need this to cleanup all the active mounts on node exit
ps *pss.Pss
} }
type SwarmAPI struct { type SwarmAPI struct {
@ -83,7 +81,7 @@ func (self *Swarm) API() *SwarmAPI {
// implements node.Service // implements node.Service
// If mockStore is not nil, it will be used as the storage for chunk data. // If mockStore is not nil, it will be used as the storage for chunk data.
// MockStore should be used only for testing. // MockStore should be used only for testing.
func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, ensClient *ethclient.Client, config *api.Config, swapEnabled, syncEnabled bool, cors string, pssEnabled bool, resourceEnabled bool, mockStore *mock.NodeStore) (self *Swarm, err error) { func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, ensClient *ethclient.Client, config *api.Config, mockStore *mock.NodeStore) (self *Swarm, err error) {
if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) { if bytes.Equal(common.FromHex(config.PublicKey), storage.ZeroKey) {
return nil, fmt.Errorf("empty public key") return nil, fmt.Errorf("empty public key")
@ -93,11 +91,9 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, ensClient *e
} }
self = &Swarm{ self = &Swarm{
config: config, config: config,
swapEnabled: swapEnabled, backend: backend,
backend: backend, privateKey: config.Swap.PrivateKey(),
privateKey: config.Swap.PrivateKey(),
corsString: cors,
} }
log.Debug(fmt.Sprintf("Setting up Swarm service components")) log.Debug(fmt.Sprintf("Setting up Swarm service components"))
@ -139,7 +135,7 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, ensClient *e
log.Debug(fmt.Sprintf("-> Content Store API")) log.Debug(fmt.Sprintf("-> Content Store API"))
// Pss = postal service over swarm (devp2p over bzz) // Pss = postal service over swarm (devp2p over bzz)
if pssEnabled { if self.config.PssEnabled {
pssparams := pss.NewPssParams(self.privateKey) pssparams := pss.NewPssParams(self.privateKey)
self.ps = pss.NewPss(to, self.dpa, pssparams) self.ps = pss.NewPss(to, self.dpa, pssparams)
if pss.IsActiveHandshake { if pss.IsActiveHandshake {
@ -162,7 +158,7 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, ensClient *e
var resourceHandler *storage.ResourceHandler var resourceHandler *storage.ResourceHandler
// if use resource updates // if use resource updates
if resourceEnabled { if self.config.ResourceEnabled {
var resourceValidator storage.ResourceValidator var resourceValidator storage.ResourceValidator
if self.dns != nil { if self.dns != nil {
resourceValidator, err = storage.NewENSValidator(config.EnsRoot, ensClient, transactOpts, storage.NewGenericResourceSigner(self.privateKey)) resourceValidator, err = storage.NewENSValidator(config.EnsRoot, ensClient, transactOpts, storage.NewGenericResourceSigner(self.privateKey))
@ -204,7 +200,7 @@ func (self *Swarm) Start(srv *p2p.Server) error {
log.Warn("Updated bzz local addr", "oaddr", fmt.Sprintf("%x", newaddr.OAddr), "uaddr", fmt.Sprintf("%x", newaddr.UAddr)) log.Warn("Updated bzz local addr", "oaddr", fmt.Sprintf("%x", newaddr.OAddr), "uaddr", fmt.Sprintf("%x", newaddr.UAddr))
// set chequebook // set chequebook
if self.swapEnabled { if self.config.SwapEnabled {
ctx := context.Background() // The initial setup has no deadline. ctx := context.Background() // The initial setup has no deadline.
err := self.SetChequebook(ctx) err := self.SetChequebook(ctx)
if err != nil { if err != nil {
@ -237,14 +233,14 @@ func (self *Swarm) Start(srv *p2p.Server) error {
addr := net.JoinHostPort(self.config.ListenAddr, self.config.Port) addr := net.JoinHostPort(self.config.ListenAddr, self.config.Port)
go httpapi.StartHttpServer(self.api, &httpapi.ServerConfig{ go httpapi.StartHttpServer(self.api, &httpapi.ServerConfig{
Addr: addr, Addr: addr,
CorsString: self.corsString, CorsString: self.config.Cors,
}) })
} }
log.Debug(fmt.Sprintf("Swarm http proxy started on port: %v", self.config.Port)) log.Debug(fmt.Sprintf("Swarm http proxy started on port: %v", self.config.Port))
if self.corsString != "" { if self.config.Cors != "" {
log.Debug(fmt.Sprintf("Swarm http proxy started with corsdomain: %v", self.corsString)) log.Debug(fmt.Sprintf("Swarm http proxy started with corsdomain: %v", self.config.Cors))
} }
return nil return nil

View file

@ -17,21 +17,29 @@
package testutil package testutil
import ( import (
"crypto/ecdsa" "context"
"io/ioutil" "io/ioutil"
"math/big"
"net/http/httptest" "net/http/httptest"
"os" "os"
"path/filepath"
"strconv" "strconv"
"testing" "testing"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/api"
httpapi "github.com/ethereum/go-ethereum/swarm/api/http" httpapi "github.com/ethereum/go-ethereum/swarm/api/http"
"github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage"
) )
type fakeBackend struct {
blocknumber int64
}
func (f *fakeBackend) BlockNumber(ctx context.Context) (big.Int, error) {
f.blocknumber++
biggie := big.NewInt(f.blocknumber)
return *biggie, nil
}
func NewTestSwarmServer(t *testing.T) *TestSwarmServer { func NewTestSwarmServer(t *testing.T) *TestSwarmServer {
dir, err := ioutil.TempDir("", "swarm-storage-test") dir, err := ioutil.TempDir("", "swarm-storage-test")
if err != nil { if err != nil {
@ -60,28 +68,8 @@ func NewTestSwarmServer(t *testing.T) *TestSwarmServer {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
ipcPath := filepath.Join(resourceDir, "test.ipc")
ipcl, err := rpc.CreateIPCListener(ipcPath)
if err != nil {
t.Fatal(err)
}
rpcServer := rpc.NewServer()
rpcServer.RegisterName("eth", &FakeRPC{})
go func() {
rpcServer.ServeListener(ipcl)
}()
rpcClean := func() {
rpcServer.Stop()
}
// connect to fake rpc rh, err := storage.NewResourceHandler(resourceDir, &testCloudStore{}, &fakeBackend{}, nil)
rpcClient, err := rpc.Dial(ipcPath)
if err != nil {
t.Fatal(err)
}
ethClient := ethclient.NewClient(rpcClient)
rh, err := storage.NewResourceHandler(resourceDir, &testCloudStore{}, ethClient, nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -94,8 +82,9 @@ func NewTestSwarmServer(t *testing.T) *TestSwarmServer {
dir: dir, dir: dir,
hasher: storage.MakeHashFunc(storage.SHA3Hash)(), hasher: storage.MakeHashFunc(storage.SHA3Hash)(),
cleanup: func() { cleanup: func() {
srv.Close()
rh.Close() rh.Close()
rpcClean() dpa.Stop()
os.RemoveAll(dir) os.RemoveAll(dir)
os.RemoveAll(resourceDir) os.RemoveAll(resourceDir)
}, },
@ -104,17 +93,14 @@ func NewTestSwarmServer(t *testing.T) *TestSwarmServer {
type TestSwarmServer struct { type TestSwarmServer struct {
*httptest.Server *httptest.Server
hasher storage.SwarmHash hasher storage.SwarmHash
privatekey *ecdsa.PrivateKey Dpa *storage.DPA
Dpa *storage.DPA dir string
dir string cleanup func()
cleanup func()
} }
func (t *TestSwarmServer) Close() { func (t *TestSwarmServer) Close() {
t.Server.Close() t.cleanup()
t.Dpa.Stop()
os.RemoveAll(t.dir)
} }
type testCloudStore struct { type testCloudStore struct {