github.com/codingfuture/orig-energi3@v0.8.4/swarm/storage/filestore.go (about)

     1  // Copyright 2018 The Energi Core Authors
     2  // Copyright 2018 The go-ethereum Authors
     3  // This file is part of the Energi Core library.
     4  //
     5  // The Energi Core library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The Energi Core library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package storage
    19  
    20  import (
    21  	"context"
    22  	"io"
    23  	"sort"
    24  	"sync"
    25  )
    26  
    27  /*
    28  FileStore provides the client API entrypoints Store and Retrieve to store and retrieve
    29  It can store anything that has a byte slice representation, so files or serialised objects etc.
    30  
    31  Storage: FileStore calls the Chunker to segment the input datastream of any size to a merkle hashed tree of chunks. The key of the root block is returned to the client.
    32  
    33  Retrieval: given the key of the root block, the FileStore retrieves the block chunks and reconstructs the original data and passes it back as a lazy reader. A lazy reader is a reader with on-demand delayed processing, i.e. the chunks needed to reconstruct a large file are only fetched and processed if that particular part of the document is actually read.
    34  
    35  As the chunker produces chunks, FileStore dispatches them to its own chunk store
    36  implementation for storage or retrieval.
    37  */
    38  
    39  const (
    40  	defaultLDBCapacity                = 5000000 // capacity for LevelDB, by default 5*10^6*4096 bytes == 20GB
    41  	defaultCacheCapacity              = 10000   // capacity for in-memory chunks' cache
    42  	defaultChunkRequestsCacheCapacity = 5000000 // capacity for container holding outgoing requests for chunks. should be set to LevelDB capacity
    43  )
    44  
    45  type FileStore struct {
    46  	ChunkStore
    47  	hashFunc SwarmHasher
    48  }
    49  
    50  type FileStoreParams struct {
    51  	Hash string
    52  }
    53  
    54  func NewFileStoreParams() *FileStoreParams {
    55  	return &FileStoreParams{
    56  		Hash: DefaultHash,
    57  	}
    58  }
    59  
    60  // for testing locally
    61  func NewLocalFileStore(datadir string, basekey []byte) (*FileStore, error) {
    62  	params := NewDefaultLocalStoreParams()
    63  	params.Init(datadir)
    64  	localStore, err := NewLocalStore(params, nil)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	localStore.Validators = append(localStore.Validators, NewContentAddressValidator(MakeHashFunc(DefaultHash)))
    69  	return NewFileStore(localStore, NewFileStoreParams()), nil
    70  }
    71  
    72  func NewFileStore(store ChunkStore, params *FileStoreParams) *FileStore {
    73  	hashFunc := MakeHashFunc(params.Hash)
    74  	return &FileStore{
    75  		ChunkStore: store,
    76  		hashFunc:   hashFunc,
    77  	}
    78  }
    79  
    80  // Retrieve is a public API. Main entry point for document retrieval directly. Used by the
    81  // FS-aware API and httpaccess
    82  // Chunk retrieval blocks on netStore requests with a timeout so reader will
    83  // report error if retrieval of chunks within requested range time out.
    84  // It returns a reader with the chunk data and whether the content was encrypted
    85  func (f *FileStore) Retrieve(ctx context.Context, addr Address) (reader *LazyChunkReader, isEncrypted bool) {
    86  	isEncrypted = len(addr) > f.hashFunc().Size()
    87  	getter := NewHasherStore(f.ChunkStore, f.hashFunc, isEncrypted)
    88  	reader = TreeJoin(ctx, addr, getter, 0)
    89  	return
    90  }
    91  
    92  // Store is a public API. Main entry point for document storage directly. Used by the
    93  // FS-aware API and httpaccess
    94  func (f *FileStore) Store(ctx context.Context, data io.Reader, size int64, toEncrypt bool) (addr Address, wait func(context.Context) error, err error) {
    95  	putter := NewHasherStore(f.ChunkStore, f.hashFunc, toEncrypt)
    96  	return PyramidSplit(ctx, data, putter, putter)
    97  }
    98  
    99  func (f *FileStore) HashSize() int {
   100  	return f.hashFunc().Size()
   101  }
   102  
   103  // GetAllReferences is a public API. This endpoint returns all chunk hashes (only) for a given file
   104  func (f *FileStore) GetAllReferences(ctx context.Context, data io.Reader, toEncrypt bool) (addrs AddressCollection, err error) {
   105  	// create a special kind of putter, which only will store the references
   106  	putter := &hashExplorer{
   107  		hasherStore: NewHasherStore(f.ChunkStore, f.hashFunc, toEncrypt),
   108  	}
   109  	// do the actual splitting anyway, no way around it
   110  	_, wait, err := PyramidSplit(ctx, data, putter, putter)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	// wait for splitting to be complete and all chunks processed
   115  	err = wait(ctx)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	// collect all references
   120  	addrs = NewAddressCollection(0)
   121  	for _, ref := range putter.references {
   122  		addrs = append(addrs, Address(ref))
   123  	}
   124  	sort.Sort(addrs)
   125  	return addrs, nil
   126  }
   127  
   128  // hashExplorer is a special kind of putter which will only store chunk references
   129  type hashExplorer struct {
   130  	*hasherStore
   131  	references []Reference
   132  	lock       sync.Mutex
   133  }
   134  
   135  // HashExplorer's Put will add just the chunk hashes to its `References`
   136  func (he *hashExplorer) Put(ctx context.Context, chunkData ChunkData) (Reference, error) {
   137  	// Need to do the actual Put, which returns the references
   138  	ref, err := he.hasherStore.Put(ctx, chunkData)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	// internally store the reference
   143  	he.lock.Lock()
   144  	he.references = append(he.references, ref)
   145  	he.lock.Unlock()
   146  	return ref, nil
   147  }