github.com/etherbanking/go-etherbanking@v1.7.1-0.20181009210156-cf649bca5aba/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-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 "encoding/binary" 21 "errors" 22 "fmt" 23 "hash" 24 "io" 25 "sync" 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 const ( 54 defaultHash = "SHA3" 55 // defaultHash = "BMTSHA3" // http://golang.org/pkg/hash/#Hash 56 // defaultHash = "SHA256" // http://golang.org/pkg/hash/#Hash 57 defaultBranches int64 = 128 58 // hashSize int64 = hasherfunc.New().Size() // hasher knows about its own length in bytes 59 // chunksize int64 = branches * hashSize // chunk is defined as this 60 ) 61 62 /* 63 Tree chunker is a concrete implementation of data chunking. 64 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. 65 66 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. 67 The hashing itself does use extra copies and allocation though, since it does need it. 68 */ 69 70 type ChunkerParams struct { 71 Branches int64 72 Hash string 73 } 74 75 func NewChunkerParams() *ChunkerParams { 76 return &ChunkerParams{ 77 Branches: defaultBranches, 78 Hash: defaultHash, 79 } 80 } 81 82 type TreeChunker struct { 83 branches int64 84 hashFunc Hasher 85 // calculated 86 hashSize int64 // self.hashFunc.New().Size() 87 chunkSize int64 // hashSize* branches 88 workerCount int 89 } 90 91 func NewTreeChunker(params *ChunkerParams) (self *TreeChunker) { 92 self = &TreeChunker{} 93 self.hashFunc = MakeHashFunc(params.Hash) 94 self.branches = params.Branches 95 self.hashSize = int64(self.hashFunc().Size()) 96 self.chunkSize = self.hashSize * self.branches 97 self.workerCount = 1 98 return 99 } 100 101 // func (self *TreeChunker) KeySize() int64 { 102 // return self.hashSize 103 // } 104 105 // String() for pretty printing 106 func (self *Chunk) String() string { 107 return fmt.Sprintf("Key: %v TreeSize: %v Chunksize: %v", self.Key.Log(), self.Size, len(self.SData)) 108 } 109 110 type hashJob struct { 111 key Key 112 chunk []byte 113 size int64 114 parentWg *sync.WaitGroup 115 } 116 117 func (self *TreeChunker) Split(data io.Reader, size int64, chunkC chan *Chunk, swg, wwg *sync.WaitGroup) (Key, error) { 118 119 if self.chunkSize <= 0 { 120 panic("chunker must be initialised") 121 } 122 123 jobC := make(chan *hashJob, 2*processors) 124 wg := &sync.WaitGroup{} 125 errC := make(chan error) 126 quitC := make(chan bool) 127 128 // wwg = workers waitgroup keeps track of hashworkers spawned by this split call 129 if wwg != nil { 130 wwg.Add(1) 131 } 132 go self.hashWorker(jobC, chunkC, errC, quitC, swg, wwg) 133 134 depth := 0 135 treeSize := self.chunkSize 136 137 // takes lowest depth such that chunksize*HashCount^(depth+1) > size 138 // 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. 139 for ; treeSize < size; treeSize *= self.branches { 140 depth++ 141 } 142 143 key := make([]byte, self.hashFunc().Size()) 144 // this waitgroup member is released after the root hash is calculated 145 wg.Add(1) 146 //launch actual recursive function passing the waitgroups 147 go self.split(depth, treeSize/self.branches, key, data, size, jobC, chunkC, errC, quitC, wg, swg, wwg) 148 149 // closes internal error channel if all subprocesses in the workgroup finished 150 go func() { 151 // waiting for all threads to finish 152 wg.Wait() 153 // if storage waitgroup is non-nil, we wait for storage to finish too 154 if swg != nil { 155 swg.Wait() 156 } 157 close(errC) 158 }() 159 160 //TODO: add a timeout 161 if err := <-errC; err != nil { 162 close(quitC) 163 return nil, err 164 } 165 166 return key, nil 167 } 168 169 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) { 170 171 for depth > 0 && size < treeSize { 172 treeSize /= self.branches 173 depth-- 174 } 175 176 if depth == 0 { 177 // leaf nodes -> content chunks 178 chunkData := make([]byte, size+8) 179 binary.LittleEndian.PutUint64(chunkData[0:8], uint64(size)) 180 var readBytes int64 181 for readBytes < size { 182 n, err := data.Read(chunkData[8+readBytes:]) 183 readBytes += int64(n) 184 if err != nil && !(err == io.EOF && readBytes == size) { 185 errC <- err 186 return 187 } 188 } 189 select { 190 case jobC <- &hashJob{key, chunkData, size, parentWg}: 191 case <-quitC: 192 } 193 return 194 } 195 // dept > 0 196 // intermediate chunk containing child nodes hashes 197 branchCnt := int64((size + treeSize - 1) / treeSize) 198 199 var chunk []byte = make([]byte, branchCnt*self.hashSize+8) 200 var pos, i int64 201 202 binary.LittleEndian.PutUint64(chunk[0:8], uint64(size)) 203 204 childrenWg := &sync.WaitGroup{} 205 var secSize int64 206 for i < branchCnt { 207 // the last item can have shorter data 208 if size-pos < treeSize { 209 secSize = size - pos 210 } else { 211 secSize = treeSize 212 } 213 // the hash of that data 214 subTreeKey := chunk[8+i*self.hashSize : 8+(i+1)*self.hashSize] 215 216 childrenWg.Add(1) 217 self.split(depth-1, treeSize/self.branches, subTreeKey, data, secSize, jobC, chunkC, errC, quitC, childrenWg, swg, wwg) 218 219 i++ 220 pos += treeSize 221 } 222 // wait for all the children to complete calculating their hashes and copying them onto sections of the chunk 223 // parentWg.Add(1) 224 // go func() { 225 childrenWg.Wait() 226 if len(jobC) > self.workerCount && self.workerCount < processors { 227 if wwg != nil { 228 wwg.Add(1) 229 } 230 self.workerCount++ 231 go self.hashWorker(jobC, chunkC, errC, quitC, swg, wwg) 232 } 233 select { 234 case jobC <- &hashJob{key, chunk, size, parentWg}: 235 case <-quitC: 236 } 237 } 238 239 func (self *TreeChunker) hashWorker(jobC chan *hashJob, chunkC chan *Chunk, errC chan error, quitC chan bool, swg, wwg *sync.WaitGroup) { 240 hasher := self.hashFunc() 241 if wwg != nil { 242 defer wwg.Done() 243 } 244 for { 245 select { 246 247 case job, ok := <-jobC: 248 if !ok { 249 return 250 } 251 // now we got the hashes in the chunk, then hash the chunks 252 hasher.Reset() 253 self.hashChunk(hasher, job, chunkC, swg) 254 case <-quitC: 255 return 256 } 257 } 258 } 259 260 // The treeChunkers own Hash hashes together 261 // - the size (of the subtree encoded in the Chunk) 262 // - the Chunk, ie. the contents read from the input reader 263 func (self *TreeChunker) hashChunk(hasher hash.Hash, job *hashJob, chunkC chan *Chunk, swg *sync.WaitGroup) { 264 hasher.Write(job.chunk) 265 h := hasher.Sum(nil) 266 newChunk := &Chunk{ 267 Key: h, 268 SData: job.chunk, 269 Size: job.size, 270 wg: swg, 271 } 272 273 // report hash of this chunk one level up (keys corresponds to the proper subslice of the parent chunk) 274 copy(job.key, h) 275 // send off new chunk to storage 276 if chunkC != nil { 277 if swg != nil { 278 swg.Add(1) 279 } 280 } 281 job.parentWg.Done() 282 283 if chunkC != nil { 284 chunkC <- newChunk 285 } 286 } 287 288 // LazyChunkReader implements LazySectionReader 289 type LazyChunkReader struct { 290 key Key // root key 291 chunkC chan *Chunk // chunk channel to send retrieve requests on 292 chunk *Chunk // size of the entire subtree 293 off int64 // offset 294 chunkSize int64 // inherit from chunker 295 branches int64 // inherit from chunker 296 hashSize int64 // inherit from chunker 297 } 298 299 // implements the Joiner interface 300 func (self *TreeChunker) Join(key Key, chunkC chan *Chunk) LazySectionReader { 301 302 return &LazyChunkReader{ 303 key: key, 304 chunkC: chunkC, 305 chunkSize: self.chunkSize, 306 branches: self.branches, 307 hashSize: self.hashSize, 308 } 309 } 310 311 // Size is meant to be called on the LazySectionReader 312 func (self *LazyChunkReader) Size(quitC chan bool) (n int64, err error) { 313 if self.chunk != nil { 314 return self.chunk.Size, nil 315 } 316 chunk := retrieve(self.key, self.chunkC, quitC) 317 if chunk == nil { 318 select { 319 case <-quitC: 320 return 0, errors.New("aborted") 321 default: 322 return 0, fmt.Errorf("root chunk not found for %v", self.key.Hex()) 323 } 324 } 325 self.chunk = chunk 326 return chunk.Size, nil 327 } 328 329 // read at can be called numerous times 330 // concurrent reads are allowed 331 // Size() needs to be called synchronously on the LazyChunkReader first 332 func (self *LazyChunkReader) ReadAt(b []byte, off int64) (read int, err error) { 333 // this is correct, a swarm doc cannot be zero length, so no EOF is expected 334 if len(b) == 0 { 335 return 0, nil 336 } 337 quitC := make(chan bool) 338 size, err := self.Size(quitC) 339 if err != nil { 340 return 0, err 341 } 342 343 errC := make(chan error) 344 345 // } 346 var treeSize int64 347 var depth int 348 // calculate depth and max treeSize 349 treeSize = self.chunkSize 350 for ; treeSize < size; treeSize *= self.branches { 351 depth++ 352 } 353 wg := sync.WaitGroup{} 354 wg.Add(1) 355 go self.join(b, off, off+int64(len(b)), depth, treeSize/self.branches, self.chunk, &wg, errC, quitC) 356 go func() { 357 wg.Wait() 358 close(errC) 359 }() 360 361 err = <-errC 362 if err != nil { 363 close(quitC) 364 365 return 0, err 366 } 367 if off+int64(len(b)) >= size { 368 return len(b), io.EOF 369 } 370 return len(b), nil 371 } 372 373 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) { 374 defer parentWg.Done() 375 // return NewDPA(&LocalStore{}) 376 377 // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) 378 379 // find appropriate block level 380 for chunk.Size < treeSize && depth > 0 { 381 treeSize /= self.branches 382 depth-- 383 } 384 385 // leaf chunk found 386 if depth == 0 { 387 extra := 8 + eoff - int64(len(chunk.SData)) 388 if extra > 0 { 389 eoff -= extra 390 } 391 copy(b, chunk.SData[8+off:8+eoff]) 392 return // simply give back the chunks reader for content chunks 393 } 394 395 // subtree 396 start := off / treeSize 397 end := (eoff + treeSize - 1) / treeSize 398 399 wg := &sync.WaitGroup{} 400 defer wg.Wait() 401 402 for i := start; i < end; i++ { 403 soff := i * treeSize 404 roff := soff 405 seoff := soff + treeSize 406 407 if soff < off { 408 soff = off 409 } 410 if seoff > eoff { 411 seoff = eoff 412 } 413 if depth > 1 { 414 wg.Wait() 415 } 416 wg.Add(1) 417 go func(j int64) { 418 childKey := chunk.SData[8+j*self.hashSize : 8+(j+1)*self.hashSize] 419 chunk := retrieve(childKey, self.chunkC, quitC) 420 if chunk == nil { 421 select { 422 case errC <- fmt.Errorf("chunk %v-%v not found", off, off+treeSize): 423 case <-quitC: 424 } 425 return 426 } 427 if soff < off { 428 soff = off 429 } 430 self.join(b[soff-off:seoff-off], soff-roff, seoff-roff, depth-1, treeSize/self.branches, chunk, wg, errC, quitC) 431 }(i) 432 } //for 433 } 434 435 // the helper method submits chunks for a key to a oueue (DPA) and 436 // block until they time out or arrive 437 // abort if quitC is readable 438 func retrieve(key Key, chunkC chan *Chunk, quitC chan bool) *Chunk { 439 chunk := &Chunk{ 440 Key: key, 441 C: make(chan bool), // close channel to signal data delivery 442 } 443 // submit chunk for retrieval 444 select { 445 case chunkC <- chunk: // submit retrieval request, someone should be listening on the other side (or we will time out globally) 446 case <-quitC: 447 return nil 448 } 449 // waiting for the chunk retrieval 450 select { // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) 451 452 case <-quitC: 453 // this is how we control process leakage (quitC is closed once join is finished (after timeout)) 454 return nil 455 case <-chunk.C: // bells are ringing, data have been delivered 456 } 457 if len(chunk.SData) == 0 { 458 return nil // chunk.Size = int64(binary.LittleEndian.Uint64(chunk.SData[0:8])) 459 460 } 461 return chunk 462 } 463 464 // Read keeps a cursor so cannot be called simulateously, see ReadAt 465 func (self *LazyChunkReader) Read(b []byte) (read int, err error) { 466 read, err = self.ReadAt(b, self.off) 467 468 self.off += int64(read) 469 return 470 } 471 472 // completely analogous to standard SectionReader implementation 473 var errWhence = errors.New("Seek: invalid whence") 474 var errOffset = errors.New("Seek: invalid offset") 475 476 func (s *LazyChunkReader) Seek(offset int64, whence int) (int64, error) { 477 switch whence { 478 default: 479 return 0, errWhence 480 case 0: 481 offset += 0 482 case 1: 483 offset += s.off 484 case 2: 485 if s.chunk == nil { //seek from the end requires rootchunk for size. call Size first 486 _, err := s.Size(nil) 487 if err != nil { 488 return 0, fmt.Errorf("can't get size: %v", err) 489 } 490 } 491 offset += s.chunk.Size 492 } 493 494 if offset < 0 { 495 return 0, errOffset 496 } 497 s.off = offset 498 return offset, nil 499 }