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  }