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 }