github.com/waltonchain/waltonchain_gwtc_src@v1.1.4-0.20201225072101-8a298c95a819/swarm/storage/chunker.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-wtc 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-wtc 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  	"encoding/binary"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"sync"
    25  	"time"
    26  )
    27  
    28  /*
    29  The distributed storage implemented in this package requires fix sized chunks of content.
    30  
    31  Chunker is the interface to a component that is responsible for disassembling and assembling larger data.
    32  
    33  TreeChunker implements a Chunker based on a tree structure defined as follows:
    34  
    35  1 each node in the tree including the root and other branching nodes are stored as a chunk.
    36  
    37  2 branching nodes encode data contents that includes the size of the dataslice covered by its entire subtree under the node as well as the hash keys of all its children :
    38  data_{i} := size(subtree_{i}) || key_{j} || key_{j+1} .... || key_{j+n-1}
    39  
    40  3 Leaf nodes encode an actual subslice of the input data.
    41  
    42  4 if data size is not more than maximum chunksize, the data is stored in a single chunk
    43    key = hash(int64(size) + data)
    44  
    45  5 if data size is more than chunksize*branches^l, but no more than chunksize*
    46    branches^(l+1), the data vector is split into slices of chunksize*
    47    branches^l length (except the last one).
    48    key = hash(int64(size) + key(slice0) + key(slice1) + ...)
    49  
    50   The underlying hash function is configurable
    51  */
    52  
    53  
    54  /*
    55  Tree chunker is a concrete implementation of data chunking.
    56  This chunker works in a simple way, it builds a tree out of the document so that each node either represents a chunk of real data or a chunk of data representing an branching non-leaf node of the tree. In particular each such non-leaf chunk will represent is a concatenation of the hash of its respective children. This scheme simultaneously guarantees data integrity as well as self addressing. Abstract nodes are transparent since their represented size component is strictly greater than their maximum data size, since they encode a subtree.
    57  
    58  If all is well it is possible to implement this by simply composing readers so that no extra allocation or buffering is necessary for the data splitting and joining. This means that in principle there can be direct IO between : memory, file system, network socket (bzz peers storage request is read from the socket). In practice there may be need for several stages of internal buffering.
    59  The hashing itself does use extra copies and allocation though, since it does need it.
    60  */
    61  
    62  var (
    63  	errAppendOppNotSuported = errors.New("Append operation not supported")
    64  	errOperationTimedOut = errors.New("operation timed out")
    65  )
    66  
    67  type TreeChunker struct {
    68  	branches int64
    69  	hashFunc SwarmHasher
    70  	// calculated
    71  	hashSize    int64 // self.hashFunc.New().Size()
    72  	chunkSize   int64 // hashSize* branches
    73  	workerCount int64 // the number of worker routines used
    74  	workerLock	sync.RWMutex // lock for the worker count
    75  }
    76  
    77  func NewTreeChunker(params *ChunkerParams) (self *TreeChunker) {
    78  	self = &TreeChunker{}
    79  	self.hashFunc = MakeHashFunc(params.Hash)
    80  	self.branches = params.Branches
    81  	self.hashSize = int64(self.hashFunc().Size())
    82  	self.chunkSize = self.hashSize * self.branches
    83  	self.workerCount = 0
    84  
    85  	return
    86  }
    87  
    88  // func (self *TreeChunker) KeySize() int64 {
    89  // 	return self.hashSize
    90  // }
    91  
    92  // String() for pretty printing
    93  func (self *Chunk) String() string {
    94  	return fmt.Sprintf("Key: %v TreeSize: %v Chunksize: %v", self.Key.Log(), self.Size, len(self.SData))
    95  }
    96  
    97  type hashJob struct {
    98  	key      Key
    99  	chunk    []byte
   100  	size     int64
   101  	parentWg *sync.WaitGroup
   102  }
   103  
   104  func (self *TreeChunker) incrementWorkerCount() {
   105  	self.workerLock.Lock()
   106  	defer self.workerLock.Unlock()
   107  	self.workerCount += 1
   108  }
   109  
   110  func (self *TreeChunker) getWorkerCount() int64 {
   111  	self.workerLock.RLock()
   112  	defer self.workerLock.RUnlock()
   113  	return self.workerCount
   114  }
   115  
   116  func (self *TreeChunker) decrementWorkerCount() {
   117  	self.workerLock.Lock()
   118  	defer self.workerLock.Unlock()
   119  	self.workerCount -= 1
   120  }
   121  
   122  func (self *TreeChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) {
   123  	if self.chunkSize <= 0 {
   124  		panic("chunker must be initialised")
   125  	}
   126  
   127  
   128  	jobC := make(chan *hashJob, 2*ChunkProcessors)
   129  	wg := &sync.WaitGroup{}
   130  	errC := make(chan error)
   131  	quitC := make(chan bool)
   132  
   133  	// wwg = workers waitgroup keeps track of hashworkers spawned by this split call
   134  	if wwg != nil {
   135  		wwg.Add(1)
   136  	}
   137  
   138  	self.incrementWorkerCount()
   139  	go self.hashWorker(jobC, chunkC, errC, quitC, swg, wwg)
   140  
   141  	depth := 0
   142  	treeSize := self.chunkSize
   143  
   144  	// takes lowest depth such that chunksize*HashCount^(depth+1) > size
   145  	// power series, will find the order of magnitude of the data size in base hashCount or numbers of levels of branching in the resulting tree.
   146  	for ; treeSize < size; treeSize *= self.branches {
   147  		depth++
   148  	}
   149  
   150  	key := make([]byte, self.hashFunc().Size())
   151  	// this waitgroup member is released after the root hash is calculated
   152  	wg.Add(1)
   153  	//launch actual recursive function passing the waitgroups
   154  	go self.split(depth, treeSize/self.branches, key, data, size, jobC, chunkC, errC, quitC, wg, swg, wwg)
   155  
   156  	// closes internal error channel if all subprocesses in the workgroup finished
   157  	go func() {
   158  		// waiting for all threads to finish
   159  		wg.Wait()
   160  		// if storage waitgroup is non-nil, we wait for storage to finish too
   161  		if swg != nil {
   162  			swg.Wait()
   163  		}
   164  		close(errC)
   165  	}()
   166  
   167  
   168  	defer close(quitC)
   169  	select {
   170  	case err := <-errC:
   171  		if err != nil {
   172  			return nil, err
   173  		}
   174  	case <-time.NewTimer(splitTimeout).C:
   175  		return nil,errOperationTimedOut
   176  	}
   177  
   178  	return key, nil
   179  }
   180  
   181  func (self *TreeChunker) split(depth int, treeSize int64, key Key, data io.Reader, size int64, jobC chan *hashJob, chunkC chan *Chunk, errC chan error, quitC chan bool, parentWg, swg, wwg *sync.WaitGroup) {
   182  
   183  	//
   184  
   185  	for depth > 0 && size < treeSize {
   186  		treeSize /= self.branches
   187  		depth--
   188  	}
   189  
   190  	if depth == 0 {
   191  		// leaf nodes -> content chunks
   192  		chunkData := make([]byte, size+8)
   193  		binary.LittleEndian.PutUint64(chunkData[0:8], uint64(size))
   194  		var readBytes int64
   195  		for readBytes < size {
   196  			n, err := data.Read(chunkData[8+readBytes:])
   197  			readBytes += int64(n)
   198  			if err != nil && !(err == io.EOF && readBytes == size) {
   199  				errC <- err
   200  				return
   201  			}
   202  		}
   203  		select {
   204  		case jobC <- &hashJob{key, chunkData, size, parentWg}:
   205  		case <-quitC:
   206  		}
   207  		return
   208  	}
   209  	// dept > 0
   210  	// intermediate chunk containing child nodes hashes
   211  	branchCnt := int64((size + treeSize - 1) / treeSize)
   212  
   213  	var chunk []byte = make([]byte, branchCnt*self.hashSize+8)
   214  	var pos, i int64
   215  
   216  	binary.LittleEndian.PutUint64(chunk[0:8], uint64(size))
   217  
   218  	childrenWg := &sync.WaitGroup{}
   219  	var secSize int64
   220  	for i < branchCnt {
   221  		// the last item can have shorter data
   222  		if size-pos < treeSize {
   223  			secSize = size - pos
   224  		} else {
   225  			secSize = treeSize
   226  		}
   227  		// the hash of that data
   228  		subTreeKey := chunk[8+i*self.hashSize : 8+(i+1)*self.hashSize]
   229  
   230  		childrenWg.Add(1)
   231  		self.split(depth-1, treeSize/self.branches, subTreeKey, data, secSize, jobC, chunkC, errC, quitC, childrenWg, swg, wwg)
   232  
   233  		i++
   234  		pos += treeSize
   235  	}
   236  	// wait for all the children to complete calculating their hashes and copying them onto sections of the chunk
   237  	// parentWg.Add(1)
   238  	// go func() {
   239  	childrenWg.Wait()
   240  
   241  	worker := self.getWorkerCount()
   242  	if int64(len(jobC)) > worker && worker < ChunkProcessors {
   243  		if wwg != nil {
   244  			wwg.Add(1)
   245  		}
   246  		self.incrementWorkerCount()
   247  		go self.hashWorker(jobC, chunkC, errC, quitC, swg, wwg)
   248  
   249  	}
   250  	select {
   251  	case jobC <- &hashJob{key, chunk, size, parentWg}:
   252  	case <-quitC:
   253  	}
   254  }
   255  
   256  func (self *TreeChunker) hashWorker(jobC chan *hashJob, chunkC chan *Chunk, errC chan error, quitC chan bool, swg, wwg *sync.WaitGroup) {
   257  	defer self.decrementWorkerCount()
   258  
   259  	hasher := self.hashFunc()
   260  	if wwg != nil {
   261  		defer wwg.Done()
   262  	}
   263  	for {
   264  		select {
   265  
   266  		case job, ok := <-jobC:
   267  			if !ok {
   268  				return
   269  			}
   270  			// now we got the hashes in the chunk, then hash the chunks
   271  			self.hashChunk(hasher, job, chunkC, swg)
   272  		case <-quitC:
   273  			return
   274  		}
   275  	}
   276  }
   277  
   278  // The treeChunkers own Hash hashes together
   279  // - the size (of the subtree encoded in the Chunk)
   280  // - the Chunk, ie. the contents read from the input reader
   281  func (self *TreeChunker) hashChunk(hasher SwarmHash, job *hashJob, chunkC chan *Chunk, swg *sync.WaitGroup) {
   282  	hasher.ResetWithLength(job.chunk[:8]) // 8 bytes of length
   283  	hasher.Write(job.chunk[8:])           // minus 8 []byte length
   284  	h := hasher.Sum(nil)
   285  
   286  	newChunk := &Chunk{
   287  		Key:   h,
   288  		SData: job.chunk,
   289  		Size:  job.size,
   290  		wg:    swg,
   291  	}
   292  
   293  	// report hash of this chunk one level up (keys corresponds to the proper subslice of the parent chunk)
   294  	copy(job.key, h)
   295  	// send off new chunk to storage
   296  	if chunkC != nil {
   297  		if swg != nil {
   298  			swg.Add(1)
   299  		}
   300  	}
   301  	job.parentWg.Done()
   302  
   303  	if chunkC != nil {
   304  		chunkC <- newChunk
   305  	}
   306  }
   307  
   308  func (self *TreeChunker) Append(key Key, data io.Reader, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) {
   309  	return nil, errAppendOppNotSuported
   310  }
   311  
   312  // LazyChunkReader implements LazySectionReader
   313  type LazyChunkReader struct {
   314  	key       Key         // root key
   315  	chunkC    chan *Chunk // chunk channel to send retrieve requests on
   316  	chunk     *Chunk      // size of the entire subtree
   317  	off       int64       // offset
   318  	chunkSize int64       // inherit from chunker
   319  	branches  int64       // inherit from chunker
   320  	hashSize  int64       // inherit from chunker
   321  }
   322  
   323  // implements the Joiner interface
   324  func (self *TreeChunker) Join(key Key, chunkC chan *Chunk) LazySectionReader {
   325  	return &LazyChunkReader{
   326  		key:       key,
   327  		chunkC:    chunkC,
   328  		chunkSize: self.chunkSize,
   329  		branches:  self.branches,
   330  		hashSize:  self.hashSize,
   331  	}
   332  }
   333  
   334  // Size is meant to be called on the LazySectionReader
   335  func (self *LazyChunkReader) Size(quitC chan bool) (n int64, err error) {
   336  	if self.chunk != nil {
   337  		return self.chunk.Size, nil
   338  	}
   339  	chunk := retrieve(self.key, self.chunkC, quitC)
   340  	if chunk == nil {
   341  		select {
   342  		case <-quitC:
   343  			return 0, errors.New("aborted")
   344  		default:
   345  			return 0, fmt.Errorf("root chunk not found for %v", self.key.Hex())
   346  		}
   347  	}
   348  	self.chunk = chunk
   349  	return chunk.Size, nil
   350  }
   351  
   352  // read at can be called numerous times
   353  // concurrent reads are allowed
   354  // Size() needs to be called synchronously on the LazyChunkReader first
   355  func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) {
   356  	// this is correct, a swarm doc cannot be zero length, so no EOF is expected
   357  	if len(b) == 0 {
   358  		return 0, nil
   359  	}
   360  	quitC := make(chan bool)
   361  	size, err := self.Size(quitC)
   362  	if err != nil {
   363  		return 0, err
   364  	}
   365  
   366  	errC := make(chan error)
   367  
   368  	// }
   369  	var treeSize int64
   370  	var depth int
   371  	// calculate depth and max treeSize
   372  	treeSize = self.chunkSize
   373  	for ; treeSize < size; treeSize *= self.branches {
   374  		depth++
   375  	}
   376  	wg := sync.WaitGroup{}
   377  	wg.Add(1)
   378  	go self.join(b, off, off+int64(len(b)), depth, treeSize/self.branches, self.chunk, &wg, errC, quitC)
   379  	go func() {
   380  		wg.Wait()
   381  		close(errC)
   382  	}()
   383  
   384  	err = <-errC
   385  	if err != nil {
   386  		close(quitC)
   387  
   388  		return 0, err
   389  	}
   390  	if off+int64(len(b)) >= size {
   391  		return len(b), io.EOF
   392  	}
   393  	return len(b), nil
   394  }
   395  
   396  func (self *LazyChunkReader) join(b []byte, off int64, eoff int64, depth int, treeSize int64, chunk *Chunk, parentWg *sync.WaitGroup, errC chan error, quitC chan bool) {
   397  	defer parentWg.Done()
   398  	// return NewDPA(&LocalStore{})
   399  
   400  	// chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
   401  
   402  	// find appropriate block level
   403  	for chunk.Size < treeSize && depth > 0 {
   404  		treeSize /= self.branches
   405  		depth--
   406  	}
   407  
   408  	// leaf chunk found
   409  	if depth == 0 {
   410  		extra := 8 + eoff - int64(len(chunk.SData))
   411  		if extra > 0 {
   412  			eoff -= extra
   413  		}
   414  		copy(b, chunk.SData[8+off:8+eoff])
   415  		return // simply give back the chunks reader for content chunks
   416  	}
   417  
   418  	// subtree
   419  	start := off / treeSize
   420  	end := (eoff + treeSize - 1) / treeSize
   421  
   422  	wg := &sync.WaitGroup{}
   423  	defer wg.Wait()
   424  
   425  	for i := start; i < end; i++ {
   426  		soff := i * treeSize
   427  		roff := soff
   428  		seoff := soff + treeSize
   429  
   430  		if soff < off {
   431  			soff = off
   432  		}
   433  		if seoff > eoff {
   434  			seoff = eoff
   435  		}
   436  		if depth > 1 {
   437  			wg.Wait()
   438  		}
   439  		wg.Add(1)
   440  		go func(j int64) {
   441  			childKey := chunk.SData[8+j*self.hashSize : 8+(j+1)*self.hashSize]
   442  			chunk := retrieve(childKey, self.chunkC, quitC)
   443  			if chunk == nil {
   444  				select {
   445  				case errC <- fmt.Errorf("chunk %v-%v not found", off, off+treeSize):
   446  				case <-quitC:
   447  				}
   448  				return
   449  			}
   450  			if soff < off {
   451  				soff = off
   452  			}
   453  			self.join(b[soff-off:seoff-off], soff-roff, seoff-roff, depth-1, treeSize/self.branches, chunk, wg, errC, quitC)
   454  		}(i)
   455  	} //for
   456  }
   457  
   458  // the helper method submits chunks for a key to a oueue (DPA) and
   459  // block until they time out or arrive
   460  // abort if quitC is readable
   461  func retrieve(key Key, chunkC chan *Chunk, quitC chan bool) *Chunk {
   462  	chunk := &Chunk{
   463  		Key: key,
   464  		C:   make(chan bool), // close channel to signal data delivery
   465  	}
   466  	// submit chunk for retrieval
   467  	select {
   468  	case chunkC <- chunk: // submit retrieval request, someone should be listening on the other side (or we will time out globally)
   469  	case <-quitC:
   470  		return nil
   471  	}
   472  	// waiting for the chunk retrieval
   473  	select { // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
   474  
   475  	case <-quitC:
   476  		// this is how we control process leakage (quitC is closed once join is finished (after timeout))
   477  		return nil
   478  	case <-chunk.C: // bells are ringing, data have been delivered
   479  	}
   480  	if len(chunk.SData) == 0 {
   481  		return nil // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8]))
   482  
   483  	}
   484  	return chunk
   485  }
   486  
   487  // Read keeps a cursor so cannot be called simulateously, see ReadAt
   488  func (self *LazyChunkReader) Read(b []byte) (read int, err error) {
   489  	read, err = self.ReadAt(b, self.off)
   490  
   491  	self.off += int64(read)
   492  	return
   493  }
   494  
   495  // completely analogous to standard SectionReader implementation
   496  var errWhence = errors.New("Seek: invalid whence")
   497  var errOffset = errors.New("Seek: invalid offset")
   498  
   499  func (s *LazyChunkReader) Seek(offset int64, whence int) (int64, error) {
   500  	switch whence {
   501  	default:
   502  		return 0, errWhence
   503  	case 0:
   504  		offset += 0
   505  	case 1:
   506  		offset += s.off
   507  	case 2:
   508  		if s.chunk == nil { //seek from the end requires rootchunk for size. call Size first
   509  			_, err := s.Size(nil)
   510  			if err != nil {
   511  				return 0, fmt.Errorf("can't get size: %v", err)
   512  			}
   513  		}
   514  		offset += s.chunk.Size
   515  	}
   516  
   517  	if offset < 0 {
   518  		return 0, errOffset
   519  	}
   520  	s.off = offset
   521  	return offset, nil
   522  }