github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/storage/hasherstore.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package storage 18 19 import ( 20 "context" 21 "fmt" 22 "sync/atomic" 23 24 "github.com/FusionFoundation/efsn/crypto/sha3" 25 ch "github.com/FusionFoundation/efsn/swarm/chunk" 26 "github.com/FusionFoundation/efsn/swarm/storage/encryption" 27 ) 28 29 type hasherStore struct { 30 store ChunkStore 31 toEncrypt bool 32 hashFunc SwarmHasher 33 hashSize int // content hash size 34 refSize int64 // reference size (content hash + possibly encryption key) 35 nrChunks uint64 // number of chunks to store 36 errC chan error // global error channel 37 doneC chan struct{} // closed by Close() call to indicate that count is the final number of chunks 38 quitC chan struct{} // closed to quit unterminated routines 39 } 40 41 // NewHasherStore creates a hasherStore object, which implements Putter and Getter interfaces. 42 // With the HasherStore you can put and get chunk data (which is just []byte) into a ChunkStore 43 // and the hasherStore will take core of encryption/decryption of data if necessary 44 func NewHasherStore(store ChunkStore, hashFunc SwarmHasher, toEncrypt bool) *hasherStore { 45 hashSize := hashFunc().Size() 46 refSize := int64(hashSize) 47 if toEncrypt { 48 refSize += encryption.KeyLength 49 } 50 51 h := &hasherStore{ 52 store: store, 53 toEncrypt: toEncrypt, 54 hashFunc: hashFunc, 55 hashSize: hashSize, 56 refSize: refSize, 57 errC: make(chan error), 58 doneC: make(chan struct{}), 59 quitC: make(chan struct{}), 60 } 61 62 return h 63 } 64 65 // Put stores the chunkData into the ChunkStore of the hasherStore and returns the reference. 66 // If hasherStore has a chunkEncryption object, the data will be encrypted. 67 // Asynchronous function, the data will not necessarily be stored when it returns. 68 func (h *hasherStore) Put(ctx context.Context, chunkData ChunkData) (Reference, error) { 69 c := chunkData 70 var encryptionKey encryption.Key 71 if h.toEncrypt { 72 var err error 73 c, encryptionKey, err = h.encryptChunkData(chunkData) 74 if err != nil { 75 return nil, err 76 } 77 } 78 chunk := h.createChunk(c) 79 h.storeChunk(ctx, chunk) 80 81 return Reference(append(chunk.Address(), encryptionKey...)), nil 82 } 83 84 // Get returns data of the chunk with the given reference (retrieved from the ChunkStore of hasherStore). 85 // If the data is encrypted and the reference contains an encryption key, it will be decrypted before 86 // return. 87 func (h *hasherStore) Get(ctx context.Context, ref Reference) (ChunkData, error) { 88 addr, encryptionKey, err := parseReference(ref, h.hashSize) 89 if err != nil { 90 return nil, err 91 } 92 93 chunk, err := h.store.Get(ctx, addr) 94 if err != nil { 95 return nil, err 96 } 97 98 chunkData := ChunkData(chunk.Data()) 99 toDecrypt := (encryptionKey != nil) 100 if toDecrypt { 101 var err error 102 chunkData, err = h.decryptChunkData(chunkData, encryptionKey) 103 if err != nil { 104 return nil, err 105 } 106 } 107 return chunkData, nil 108 } 109 110 // Close indicates that no more chunks will be put with the hasherStore, so the Wait 111 // function can return when all the previously put chunks has been stored. 112 func (h *hasherStore) Close() { 113 close(h.doneC) 114 } 115 116 // Wait returns when 117 // 1) the Close() function has been called and 118 // 2) all the chunks which has been Put has been stored 119 func (h *hasherStore) Wait(ctx context.Context) error { 120 defer close(h.quitC) 121 var nrStoredChunks uint64 // number of stored chunks 122 var done bool 123 doneC := h.doneC 124 for { 125 select { 126 // if context is done earlier, just return with the error 127 case <-ctx.Done(): 128 return ctx.Err() 129 // doneC is closed if all chunks have been submitted, from then we just wait until all of them are also stored 130 case <-doneC: 131 done = true 132 doneC = nil 133 // a chunk has been stored, if err is nil, then successfully, so increase the stored chunk counter 134 case err := <-h.errC: 135 if err != nil { 136 return err 137 } 138 nrStoredChunks++ 139 } 140 // if all the chunks have been submitted and all of them are stored, then we can return 141 if done { 142 if nrStoredChunks >= atomic.LoadUint64(&h.nrChunks) { 143 return nil 144 } 145 } 146 } 147 } 148 149 func (h *hasherStore) createHash(chunkData ChunkData) Address { 150 hasher := h.hashFunc() 151 hasher.ResetWithLength(chunkData[:8]) // 8 bytes of length 152 hasher.Write(chunkData[8:]) // minus 8 []byte length 153 return hasher.Sum(nil) 154 } 155 156 func (h *hasherStore) createChunk(chunkData ChunkData) *chunk { 157 hash := h.createHash(chunkData) 158 chunk := NewChunk(hash, chunkData) 159 return chunk 160 } 161 162 func (h *hasherStore) encryptChunkData(chunkData ChunkData) (ChunkData, encryption.Key, error) { 163 if len(chunkData) < 8 { 164 return nil, nil, fmt.Errorf("Invalid ChunkData, min length 8 got %v", len(chunkData)) 165 } 166 167 key, encryptedSpan, encryptedData, err := h.encrypt(chunkData) 168 if err != nil { 169 return nil, nil, err 170 } 171 c := make(ChunkData, len(encryptedSpan)+len(encryptedData)) 172 copy(c[:8], encryptedSpan) 173 copy(c[8:], encryptedData) 174 return c, key, nil 175 } 176 177 func (h *hasherStore) decryptChunkData(chunkData ChunkData, encryptionKey encryption.Key) (ChunkData, error) { 178 if len(chunkData) < 8 { 179 return nil, fmt.Errorf("Invalid ChunkData, min length 8 got %v", len(chunkData)) 180 } 181 182 decryptedSpan, decryptedData, err := h.decrypt(chunkData, encryptionKey) 183 if err != nil { 184 return nil, err 185 } 186 187 // removing extra bytes which were just added for padding 188 length := ChunkData(decryptedSpan).Size() 189 for length > ch.DefaultSize { 190 length = length + (ch.DefaultSize - 1) 191 length = length / ch.DefaultSize 192 length *= uint64(h.refSize) 193 } 194 195 c := make(ChunkData, length+8) 196 copy(c[:8], decryptedSpan) 197 copy(c[8:], decryptedData[:length]) 198 199 return c, nil 200 } 201 202 func (h *hasherStore) RefSize() int64 { 203 return h.refSize 204 } 205 206 func (h *hasherStore) encrypt(chunkData ChunkData) (encryption.Key, []byte, []byte, error) { 207 key := encryption.GenerateRandomKey(encryption.KeyLength) 208 encryptedSpan, err := h.newSpanEncryption(key).Encrypt(chunkData[:8]) 209 if err != nil { 210 return nil, nil, nil, err 211 } 212 encryptedData, err := h.newDataEncryption(key).Encrypt(chunkData[8:]) 213 if err != nil { 214 return nil, nil, nil, err 215 } 216 return key, encryptedSpan, encryptedData, nil 217 } 218 219 func (h *hasherStore) decrypt(chunkData ChunkData, key encryption.Key) ([]byte, []byte, error) { 220 encryptedSpan, err := h.newSpanEncryption(key).Encrypt(chunkData[:8]) 221 if err != nil { 222 return nil, nil, err 223 } 224 encryptedData, err := h.newDataEncryption(key).Encrypt(chunkData[8:]) 225 if err != nil { 226 return nil, nil, err 227 } 228 return encryptedSpan, encryptedData, nil 229 } 230 231 func (h *hasherStore) newSpanEncryption(key encryption.Key) encryption.Encryption { 232 return encryption.New(key, 0, uint32(ch.DefaultSize/h.refSize), sha3.NewKeccak256) 233 } 234 235 func (h *hasherStore) newDataEncryption(key encryption.Key) encryption.Encryption { 236 return encryption.New(key, int(ch.DefaultSize), 0, sha3.NewKeccak256) 237 } 238 239 func (h *hasherStore) storeChunk(ctx context.Context, chunk *chunk) { 240 atomic.AddUint64(&h.nrChunks, 1) 241 go func() { 242 select { 243 case h.errC <- h.store.Put(ctx, chunk): 244 case <-h.quitC: 245 } 246 }() 247 } 248 249 func parseReference(ref Reference, hashSize int) (Address, encryption.Key, error) { 250 encryptedRefLength := hashSize + encryption.KeyLength 251 switch len(ref) { 252 case AddressLength: 253 return Address(ref), nil, nil 254 case encryptedRefLength: 255 encKeyIdx := len(ref) - encryption.KeyLength 256 return Address(ref[:encKeyIdx]), encryption.Key(ref[encKeyIdx:]), nil 257 default: 258 return nil, nil, fmt.Errorf("Invalid reference length, expected %v or %v got %v", hashSize, encryptedRefLength, len(ref)) 259 } 260 }