swarm: push tags integration - request flow
swarm/api: integrate tags to count chunks being split and stored swarm/api/http: integrate tags in middleware for HTTP `POST` calls and assert chunks being calculated and counted correctly swarm: remove deprecated and unused code, add swarm hash to DoneSplit signature, remove calls to the api client from the http package
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/swarm/chunk"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
@@ -42,8 +43,10 @@ type chunkerTester struct {
|
||||
t test
|
||||
}
|
||||
|
||||
var mockTag = chunk.NewTag(0, "mock-tag", 0)
|
||||
|
||||
func newTestHasherStore(store ChunkStore, hash string) *hasherStore {
|
||||
return NewHasherStore(store, MakeHashFunc(hash), false)
|
||||
return NewHasherStore(store, MakeHashFunc(hash), false, chunk.NewTag(0, "test-tag", 0))
|
||||
}
|
||||
|
||||
func testRandomBrokenData(n int, tester *chunkerTester) {
|
||||
@@ -91,7 +94,7 @@ func testRandomData(usePyramid bool, hash string, n int, tester *chunkerTester)
|
||||
var err error
|
||||
ctx := context.TODO()
|
||||
if usePyramid {
|
||||
addr, wait, err = PyramidSplit(ctx, data, putGetter, putGetter)
|
||||
addr, wait, err = PyramidSplit(ctx, data, putGetter, putGetter, mockTag)
|
||||
} else {
|
||||
addr, wait, err = TreeSplit(ctx, data, int64(n), putGetter)
|
||||
}
|
||||
@@ -188,7 +191,7 @@ func TestDataAppend(t *testing.T) {
|
||||
putGetter := newTestHasherStore(store, SHA3Hash)
|
||||
|
||||
ctx := context.TODO()
|
||||
addr, wait, err := PyramidSplit(ctx, data, putGetter, putGetter)
|
||||
addr, wait, err := PyramidSplit(ctx, data, putGetter, putGetter, mockTag)
|
||||
if err != nil {
|
||||
tester.t.Fatalf(err.Error())
|
||||
}
|
||||
@@ -208,7 +211,7 @@ func TestDataAppend(t *testing.T) {
|
||||
}
|
||||
|
||||
putGetter = newTestHasherStore(store, SHA3Hash)
|
||||
newAddr, wait, err := PyramidAppend(ctx, addr, appendData, putGetter, putGetter)
|
||||
newAddr, wait, err := PyramidAppend(ctx, addr, appendData, putGetter, putGetter, mockTag)
|
||||
if err != nil {
|
||||
tester.t.Fatalf(err.Error())
|
||||
}
|
||||
@@ -278,7 +281,7 @@ func benchmarkSplitJoin(n int, t *testing.B) {
|
||||
|
||||
putGetter := newTestHasherStore(NewMapChunkStore(), SHA3Hash)
|
||||
ctx := context.TODO()
|
||||
key, wait, err := PyramidSplit(ctx, data, putGetter, putGetter)
|
||||
key, wait, err := PyramidSplit(ctx, data, putGetter, putGetter, mockTag)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
@@ -335,7 +338,7 @@ func benchmarkSplitPyramidBMT(n int, t *testing.B) {
|
||||
putGetter := newTestHasherStore(&FakeChunkStore{}, BMTHash)
|
||||
|
||||
ctx := context.Background()
|
||||
_, wait, err := PyramidSplit(ctx, data, putGetter, putGetter)
|
||||
_, wait, err := PyramidSplit(ctx, data, putGetter, putGetter, mockTag)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
@@ -353,7 +356,7 @@ func benchmarkSplitPyramidSHA3(n int, t *testing.B) {
|
||||
putGetter := newTestHasherStore(&FakeChunkStore{}, SHA3Hash)
|
||||
|
||||
ctx := context.Background()
|
||||
_, wait, err := PyramidSplit(ctx, data, putGetter, putGetter)
|
||||
_, wait, err := PyramidSplit(ctx, data, putGetter, putGetter, mockTag)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
@@ -374,7 +377,7 @@ func benchmarkSplitAppendPyramid(n, m int, t *testing.B) {
|
||||
putGetter := newTestHasherStore(store, SHA3Hash)
|
||||
|
||||
ctx := context.Background()
|
||||
key, wait, err := PyramidSplit(ctx, data, putGetter, putGetter)
|
||||
key, wait, err := PyramidSplit(ctx, data, putGetter, putGetter, mockTag)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
@@ -384,7 +387,7 @@ func benchmarkSplitAppendPyramid(n, m int, t *testing.B) {
|
||||
}
|
||||
|
||||
putGetter = newTestHasherStore(store, SHA3Hash)
|
||||
_, wait, err = PyramidAppend(ctx, key, data1, putGetter, putGetter)
|
||||
_, wait, err = PyramidAppend(ctx, key, data1, putGetter, putGetter, mockTag)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
@@ -47,6 +47,7 @@ const (
|
||||
type FileStore struct {
|
||||
ChunkStore
|
||||
hashFunc SwarmHasher
|
||||
tags *chunk.Tags
|
||||
}
|
||||
|
||||
type FileStoreParams struct {
|
||||
@@ -60,19 +61,20 @@ func NewFileStoreParams() *FileStoreParams {
|
||||
}
|
||||
|
||||
// for testing locally
|
||||
func NewLocalFileStore(datadir string, basekey []byte) (*FileStore, error) {
|
||||
func NewLocalFileStore(datadir string, basekey []byte, tags *chunk.Tags) (*FileStore, error) {
|
||||
localStore, err := localstore.New(datadir, basekey, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewFileStore(chunk.NewValidatorStore(localStore, NewContentAddressValidator(MakeHashFunc(DefaultHash))), NewFileStoreParams()), nil
|
||||
return NewFileStore(chunk.NewValidatorStore(localStore, NewContentAddressValidator(MakeHashFunc(DefaultHash))), NewFileStoreParams(), tags), nil
|
||||
}
|
||||
|
||||
func NewFileStore(store ChunkStore, params *FileStoreParams) *FileStore {
|
||||
func NewFileStore(store ChunkStore, params *FileStoreParams, tags *chunk.Tags) *FileStore {
|
||||
hashFunc := MakeHashFunc(params.Hash)
|
||||
return &FileStore{
|
||||
ChunkStore: store,
|
||||
hashFunc: hashFunc,
|
||||
tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +85,11 @@ func NewFileStore(store ChunkStore, params *FileStoreParams) *FileStore {
|
||||
// It returns a reader with the chunk data and whether the content was encrypted
|
||||
func (f *FileStore) Retrieve(ctx context.Context, addr Address) (reader *LazyChunkReader, isEncrypted bool) {
|
||||
isEncrypted = len(addr) > f.hashFunc().Size()
|
||||
getter := NewHasherStore(f.ChunkStore, f.hashFunc, isEncrypted)
|
||||
tag, err := f.tags.GetFromContext(ctx)
|
||||
if err != nil {
|
||||
tag = chunk.NewTag(0, "ephemeral-retrieval-tag", 0)
|
||||
}
|
||||
getter := NewHasherStore(f.ChunkStore, f.hashFunc, isEncrypted, tag)
|
||||
reader = TreeJoin(ctx, addr, getter, 0)
|
||||
return
|
||||
}
|
||||
@@ -91,8 +97,17 @@ func (f *FileStore) Retrieve(ctx context.Context, addr Address) (reader *LazyChu
|
||||
// Store is a public API. Main entry point for document storage directly. Used by the
|
||||
// FS-aware API and httpaccess
|
||||
func (f *FileStore) Store(ctx context.Context, data io.Reader, size int64, toEncrypt bool) (addr Address, wait func(context.Context) error, err error) {
|
||||
putter := NewHasherStore(f.ChunkStore, f.hashFunc, toEncrypt)
|
||||
return PyramidSplit(ctx, data, putter, putter)
|
||||
tag, err := f.tags.GetFromContext(ctx)
|
||||
if err != nil {
|
||||
// some of the parts of the codebase, namely the manifest trie, do not store the context
|
||||
// of the original request nor the tag with the trie, recalculating the trie hence
|
||||
// loses the tag uid. thus we create an ephemeral tag here for that purpose
|
||||
|
||||
tag = chunk.NewTag(0, "", 0)
|
||||
//return nil, nil, err
|
||||
}
|
||||
putter := NewHasherStore(f.ChunkStore, f.hashFunc, toEncrypt, tag)
|
||||
return PyramidSplit(ctx, data, putter, putter, tag)
|
||||
}
|
||||
|
||||
func (f *FileStore) HashSize() int {
|
||||
@@ -101,12 +116,14 @@ func (f *FileStore) HashSize() int {
|
||||
|
||||
// GetAllReferences is a public API. This endpoint returns all chunk hashes (only) for a given file
|
||||
func (f *FileStore) GetAllReferences(ctx context.Context, data io.Reader, toEncrypt bool) (addrs AddressCollection, err error) {
|
||||
tag := chunk.NewTag(0, "ephemeral-tag", 0) //this tag is just a mock ephemeral tag since we don't want to save these results
|
||||
|
||||
// create a special kind of putter, which only will store the references
|
||||
putter := &hashExplorer{
|
||||
hasherStore: NewHasherStore(f.ChunkStore, f.hashFunc, toEncrypt),
|
||||
hasherStore: NewHasherStore(f.ChunkStore, f.hashFunc, toEncrypt, tag),
|
||||
}
|
||||
// do the actual splitting anyway, no way around it
|
||||
_, wait, err := PyramidSplit(ctx, data, putter, putter)
|
||||
_, wait, err := PyramidSplit(ctx, data, putter, putter, tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/swarm/chunk"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/localstore"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
)
|
||||
@@ -48,7 +49,7 @@ func testFileStoreRandom(toEncrypt bool, t *testing.T) {
|
||||
}
|
||||
defer localStore.Close()
|
||||
|
||||
fileStore := NewFileStore(localStore, NewFileStoreParams())
|
||||
fileStore := NewFileStore(localStore, NewFileStoreParams(), chunk.NewTags())
|
||||
|
||||
slice := testutil.RandomBytes(1, testDataSize)
|
||||
ctx := context.TODO()
|
||||
@@ -113,7 +114,7 @@ func testFileStoreCapacity(toEncrypt bool, t *testing.T) {
|
||||
}
|
||||
defer localStore.Close()
|
||||
|
||||
fileStore := NewFileStore(localStore, NewFileStoreParams())
|
||||
fileStore := NewFileStore(localStore, NewFileStoreParams(), chunk.NewTags())
|
||||
slice := testutil.RandomBytes(1, testDataSize)
|
||||
ctx := context.TODO()
|
||||
key, wait, err := fileStore.Store(ctx, bytes.NewReader(slice), testDataSize, toEncrypt)
|
||||
@@ -182,7 +183,7 @@ func TestGetAllReferences(t *testing.T) {
|
||||
}
|
||||
defer localStore.Close()
|
||||
|
||||
fileStore := NewFileStore(localStore, NewFileStoreParams())
|
||||
fileStore := NewFileStore(localStore, NewFileStoreParams(), chunk.NewTags())
|
||||
|
||||
// testRuns[i] and expectedLen[i] are dataSize and expected length respectively
|
||||
testRuns := []int{1024, 8192, 16000, 30000, 1000000}
|
||||
|
@@ -28,6 +28,7 @@ import (
|
||||
|
||||
type hasherStore struct {
|
||||
store ChunkStore
|
||||
tag *chunk.Tag
|
||||
toEncrypt bool
|
||||
hashFunc SwarmHasher
|
||||
hashSize int // content hash size
|
||||
@@ -44,7 +45,7 @@ type hasherStore struct {
|
||||
// NewHasherStore creates a hasherStore object, which implements Putter and Getter interfaces.
|
||||
// With the HasherStore you can put and get chunk data (which is just []byte) into a ChunkStore
|
||||
// and the hasherStore will take core of encryption/decryption of data if necessary
|
||||
func NewHasherStore(store ChunkStore, hashFunc SwarmHasher, toEncrypt bool) *hasherStore {
|
||||
func NewHasherStore(store ChunkStore, hashFunc SwarmHasher, toEncrypt bool, tag *chunk.Tag) *hasherStore {
|
||||
hashSize := hashFunc().Size()
|
||||
refSize := int64(hashSize)
|
||||
if toEncrypt {
|
||||
@@ -53,6 +54,7 @@ func NewHasherStore(store ChunkStore, hashFunc SwarmHasher, toEncrypt bool) *has
|
||||
|
||||
h := &hasherStore{
|
||||
store: store,
|
||||
tag: tag,
|
||||
toEncrypt: toEncrypt,
|
||||
hashFunc: hashFunc,
|
||||
hashSize: hashSize,
|
||||
@@ -242,7 +244,11 @@ func (h *hasherStore) newDataEncryption(key encryption.Key) encryption.Encryptio
|
||||
func (h *hasherStore) storeChunk(ctx context.Context, ch Chunk) {
|
||||
atomic.AddUint64(&h.nrChunks, 1)
|
||||
go func() {
|
||||
_, err := h.store.Put(ctx, chunk.ModePutUpload, ch)
|
||||
seen, err := h.store.Put(ctx, chunk.ModePutUpload, ch)
|
||||
h.tag.Inc(chunk.StateStored)
|
||||
if seen {
|
||||
h.tag.Inc(chunk.StateSeen)
|
||||
}
|
||||
select {
|
||||
case h.errC <- err:
|
||||
case <-h.quitC:
|
||||
|
@@ -43,7 +43,7 @@ func TestHasherStore(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
chunkStore := NewMapChunkStore()
|
||||
hasherStore := NewHasherStore(chunkStore, MakeHashFunc(DefaultHash), tt.toEncrypt)
|
||||
hasherStore := NewHasherStore(chunkStore, MakeHashFunc(DefaultHash), tt.toEncrypt, chunk.NewTag(0, "test-tag", 0))
|
||||
|
||||
// Put two random chunks into the hasherStore
|
||||
chunkData1 := GenerateRandomChunk(int64(tt.chunkLength)).Data()
|
||||
|
@@ -96,12 +96,12 @@ func NewPyramidSplitterParams(addr Address, reader io.Reader, putter Putter, get
|
||||
When splitting, data is given as a SectionReader, and the key is a hashSize long byte slice (Address), the root hash of the entire content will fill this once processing finishes.
|
||||
New chunks to store are store using the putter which the caller provides.
|
||||
*/
|
||||
func PyramidSplit(ctx context.Context, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) {
|
||||
return NewPyramidSplitter(NewPyramidSplitterParams(nil, reader, putter, getter, chunk.DefaultSize)).Split(ctx)
|
||||
func PyramidSplit(ctx context.Context, reader io.Reader, putter Putter, getter Getter, tag *chunk.Tag) (Address, func(context.Context) error, error) {
|
||||
return NewPyramidSplitter(NewPyramidSplitterParams(nil, reader, putter, getter, chunk.DefaultSize), tag).Split(ctx)
|
||||
}
|
||||
|
||||
func PyramidAppend(ctx context.Context, addr Address, reader io.Reader, putter Putter, getter Getter) (Address, func(context.Context) error, error) {
|
||||
return NewPyramidSplitter(NewPyramidSplitterParams(addr, reader, putter, getter, chunk.DefaultSize)).Append(ctx)
|
||||
func PyramidAppend(ctx context.Context, addr Address, reader io.Reader, putter Putter, getter Getter, tag *chunk.Tag) (Address, func(context.Context) error, error) {
|
||||
return NewPyramidSplitter(NewPyramidSplitterParams(addr, reader, putter, getter, chunk.DefaultSize), tag).Append(ctx)
|
||||
}
|
||||
|
||||
// Entry to create a tree node
|
||||
@@ -142,6 +142,7 @@ type PyramidChunker struct {
|
||||
putter Putter
|
||||
getter Getter
|
||||
key Address
|
||||
tag *chunk.Tag
|
||||
workerCount int64
|
||||
workerLock sync.RWMutex
|
||||
jobC chan *chunkJob
|
||||
@@ -152,7 +153,7 @@ type PyramidChunker struct {
|
||||
chunkLevel [][]*TreeEntry
|
||||
}
|
||||
|
||||
func NewPyramidSplitter(params *PyramidSplitterParams) (pc *PyramidChunker) {
|
||||
func NewPyramidSplitter(params *PyramidSplitterParams, tag *chunk.Tag) (pc *PyramidChunker) {
|
||||
pc = &PyramidChunker{}
|
||||
pc.reader = params.reader
|
||||
pc.hashSize = params.hashSize
|
||||
@@ -161,6 +162,7 @@ func NewPyramidSplitter(params *PyramidSplitterParams) (pc *PyramidChunker) {
|
||||
pc.putter = params.putter
|
||||
pc.getter = params.getter
|
||||
pc.key = params.addr
|
||||
pc.tag = tag
|
||||
pc.workerCount = 0
|
||||
pc.jobC = make(chan *chunkJob, 2*ChunkProcessors)
|
||||
pc.wg = &sync.WaitGroup{}
|
||||
@@ -273,6 +275,7 @@ func (pc *PyramidChunker) processor(ctx context.Context, id int64) {
|
||||
return
|
||||
}
|
||||
pc.processChunk(ctx, id, job)
|
||||
pc.tag.Inc(chunk.StateSplit)
|
||||
case <-pc.quitC:
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user