github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/swarm/bmt/bmt.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 bmt provides a binary merkle tree implementation used for swarm chunk hash 18 package bmt 19 20 import ( 21 "fmt" 22 "hash" 23 "strings" 24 "sync" 25 "sync/atomic" 26 ) 27 28 /* 29 Binary Merkle Tree Hash is a hash function over arbitrary datachunks of limited size. 30 It is defined as the root hash of the binary merkle tree built over fixed size segments 31 of the underlying chunk using any base hash function (e.g., keccak 256 SHA3). 32 Chunks with data shorter than the fixed size are hashed as if they had zero padding. 33 34 BMT hash is used as the chunk hash function in swarm which in turn is the basis for the 35 128 branching swarm hash http://swarm-guide.readthedocs.io/en/latest/architecture.html#swarm-hash 36 37 The BMT is optimal for providing compact inclusion proofs, i.e. prove that a 38 segment is a substring of a chunk starting at a particular offset. 39 The size of the underlying segments is fixed to the size of the base hash (called the resolution 40 of the BMT hash), Using Keccak256 SHA3 hash is 32 bytes, the EVM word size to optimize for on-chain BMT verification 41 as well as the hash size optimal for inclusion proofs in the merkle tree of the swarm hash. 42 43 Two implementations are provided: 44 45 * RefHasher is optimized for code simplicity and meant as a reference implementation 46 that is simple to understand 47 * Hasher is optimized for speed taking advantage of concurrency with minimalistic 48 control structure to coordinate the concurrent routines 49 50 BMT Hasher implements the following interfaces 51 * standard golang hash.Hash - synchronous, reusable 52 * SwarmHash - SumWithSpan provided 53 * io.Writer - synchronous left-to-right datawriter 54 * AsyncWriter - concurrent section writes and asynchronous Sum call 55 */ 56 57 const ( 58 // SegmentCount is the maximum number of segments of the underlying chunk 59 // Should be equal to max-chunk-data-size / hash-size 60 SegmentCount = 128 61 // PoolSize is the maximum number of bmt trees used by the hashers, i.e, 62 // the maximum number of concurrent BMT hashing operations performed by the same hasher 63 PoolSize = 8 64 ) 65 66 // BaseHasherFunc is a hash.Hash constructor function used for the base hash of the BMT. 67 // implemented by Keccak256 SHA3 sha3.NewKeccak256 68 type BaseHasherFunc func() hash.Hash 69 70 // Hasher a reusable hasher for fixed maximum size chunks representing a BMT 71 // - implements the hash.Hash interface 72 // - reuses a pool of trees for amortised memory allocation and resource control 73 // - supports order-agnostic concurrent segment writes and section (double segment) writes 74 // as well as sequential read and write 75 // - the same hasher instance must not be called concurrently on more than one chunk 76 // - the same hasher instance is synchronously reuseable 77 // - Sum gives back the tree to the pool and guaranteed to leave 78 // the tree and itself in a state reusable for hashing a new chunk 79 // - generates and verifies segment inclusion proofs (TODO:) 80 type Hasher struct { 81 pool *TreePool // BMT resource pool 82 bmt *tree // prebuilt BMT resource for flowcontrol and proofs 83 } 84 85 // New creates a reusable BMT Hasher that 86 // pulls a new tree from a resource pool for hashing each chunk 87 func New(p *TreePool) *Hasher { 88 return &Hasher{ 89 pool: p, 90 } 91 } 92 93 // TreePool provides a pool of trees used as resources by the BMT Hasher. 94 // A tree popped from the pool is guaranteed to have a clean state ready 95 // for hashing a new chunk. 96 type TreePool struct { 97 lock sync.Mutex 98 c chan *tree // the channel to obtain a resource from the pool 99 hasher BaseHasherFunc // base hasher to use for the BMT levels 100 SegmentSize int // size of leaf segments, stipulated to be = hash size 101 SegmentCount int // the number of segments on the base level of the BMT 102 Capacity int // pool capacity, controls concurrency 103 Depth int // depth of the bmt trees = int(log2(segmentCount))+1 104 Size int // the total length of the data (count * size) 105 count int // current count of (ever) allocated resources 106 zerohashes [][]byte // lookup table for predictable padding subtrees for all levels 107 } 108 109 // NewTreePool creates a tree pool with hasher, segment size, segment count and capacity 110 // on Hasher.getTree it reuses free trees or creates a new one if capacity is not reached 111 func NewTreePool(hasher BaseHasherFunc, segmentCount, capacity int) *TreePool { 112 // initialises the zerohashes lookup table 113 depth := calculateDepthFor(segmentCount) 114 segmentSize := hasher().Size() 115 zerohashes := make([][]byte, depth+1) 116 zeros := make([]byte, segmentSize) 117 zerohashes[0] = zeros 118 h := hasher() 119 for i := 1; i < depth+1; i++ { 120 zeros = doSum(h, nil, zeros, zeros) 121 zerohashes[i] = zeros 122 } 123 return &TreePool{ 124 c: make(chan *tree, capacity), 125 hasher: hasher, 126 SegmentSize: segmentSize, 127 SegmentCount: segmentCount, 128 Capacity: capacity, 129 Size: segmentCount * segmentSize, 130 Depth: depth, 131 zerohashes: zerohashes, 132 } 133 } 134 135 // Drain drains the pool until it has no more than n resources 136 func (p *TreePool) Drain(n int) { 137 p.lock.Lock() 138 defer p.lock.Unlock() 139 for len(p.c) > n { 140 <-p.c 141 p.count-- 142 } 143 } 144 145 // Reserve is blocking until it returns an available tree 146 // it reuses free trees or creates a new one if size is not reached 147 // TODO: should use a context here 148 func (p *TreePool) reserve() *tree { 149 p.lock.Lock() 150 defer p.lock.Unlock() 151 var t *tree 152 if p.count == p.Capacity { 153 return <-p.c 154 } 155 select { 156 case t = <-p.c: 157 default: 158 t = newTree(p.SegmentSize, p.Depth, p.hasher) 159 p.count++ 160 } 161 return t 162 } 163 164 // release gives back a tree to the pool. 165 // this tree is guaranteed to be in reusable state 166 func (p *TreePool) release(t *tree) { 167 p.c <- t // can never fail ... 168 } 169 170 // tree is a reusable control structure representing a BMT 171 // organised in a binary tree 172 // Hasher uses a TreePool to obtain a tree for each chunk hash 173 // the tree is 'locked' while not in the pool 174 type tree struct { 175 leaves []*node // leaf nodes of the tree, other nodes accessible via parent links 176 cursor int // index of rightmost currently open segment 177 offset int // offset (cursor position) within currently open segment 178 section []byte // the rightmost open section (double segment) 179 result chan []byte // result channel 180 span []byte // The span of the data subsumed under the chunk 181 } 182 183 // node is a reuseable segment hasher representing a node in a BMT 184 type node struct { 185 isLeft bool // whether it is left side of the parent double segment 186 parent *node // pointer to parent node in the BMT 187 state int32 // atomic increment impl concurrent boolean toggle 188 left, right []byte // this is where the two children sections are written 189 hasher hash.Hash // preconstructed hasher on nodes 190 } 191 192 // newNode constructs a segment hasher node in the BMT (used by newTree) 193 func newNode(index int, parent *node, hasher hash.Hash) *node { 194 return &node{ 195 parent: parent, 196 isLeft: index%2 == 0, 197 hasher: hasher, 198 } 199 } 200 201 // Draw draws the BMT (badly) 202 func (t *tree) draw(hash []byte) string { 203 var left, right []string 204 var anc []*node 205 for i, n := range t.leaves { 206 left = append(left, fmt.Sprintf("%v", hashstr(n.left))) 207 if i%2 == 0 { 208 anc = append(anc, n.parent) 209 } 210 right = append(right, fmt.Sprintf("%v", hashstr(n.right))) 211 } 212 anc = t.leaves 213 var hashes [][]string 214 for l := 0; len(anc) > 0; l++ { 215 var nodes []*node 216 hash := []string{""} 217 for i, n := range anc { 218 hash = append(hash, fmt.Sprintf("%v|%v", hashstr(n.left), hashstr(n.right))) 219 if i%2 == 0 && n.parent != nil { 220 nodes = append(nodes, n.parent) 221 } 222 } 223 hash = append(hash, "") 224 hashes = append(hashes, hash) 225 anc = nodes 226 } 227 hashes = append(hashes, []string{"", fmt.Sprintf("%v", hashstr(hash)), ""}) 228 total := 60 229 del := " " 230 var rows []string 231 for i := len(hashes) - 1; i >= 0; i-- { 232 var textlen int 233 hash := hashes[i] 234 for _, s := range hash { 235 textlen += len(s) 236 } 237 if total < textlen { 238 total = textlen + len(hash) 239 } 240 delsize := (total - textlen) / (len(hash) - 1) 241 if delsize > len(del) { 242 delsize = len(del) 243 } 244 row := fmt.Sprintf("%v: %v", len(hashes)-i-1, strings.Join(hash, del[:delsize])) 245 rows = append(rows, row) 246 247 } 248 rows = append(rows, strings.Join(left, " ")) 249 rows = append(rows, strings.Join(right, " ")) 250 return strings.Join(rows, "\n") + "\n" 251 } 252 253 // newTree initialises a tree by building up the nodes of a BMT 254 // - segment size is stipulated to be the size of the hash 255 func newTree(segmentSize, depth int, hashfunc func() hash.Hash) *tree { 256 n := newNode(0, nil, hashfunc()) 257 prevlevel := []*node{n} 258 // iterate over levels and creates 2^(depth-level) nodes 259 // the 0 level is on double segment sections so we start at depth - 2 since 260 count := 2 261 for level := depth - 2; level >= 0; level-- { 262 nodes := make([]*node, count) 263 for i := 0; i < count; i++ { 264 parent := prevlevel[i/2] 265 var hasher hash.Hash 266 if level == 0 { 267 hasher = hashfunc() 268 } 269 nodes[i] = newNode(i, parent, hasher) 270 } 271 prevlevel = nodes 272 count *= 2 273 } 274 // the datanode level is the nodes on the last level 275 return &tree{ 276 leaves: prevlevel, 277 result: make(chan []byte), 278 section: make([]byte, 2*segmentSize), 279 } 280 } 281 282 // methods needed to implement hash.Hash 283 284 // Size returns the size 285 func (h *Hasher) Size() int { 286 return h.pool.SegmentSize 287 } 288 289 // BlockSize returns the block size 290 func (h *Hasher) BlockSize() int { 291 return 2 * h.pool.SegmentSize 292 } 293 294 // Sum returns the BMT root hash of the buffer 295 // using Sum presupposes sequential synchronous writes (io.Writer interface) 296 // hash.Hash interface Sum method appends the byte slice to the underlying 297 // data before it calculates and returns the hash of the chunk 298 // caller must make sure Sum is not called concurrently with Write, writeSection 299 func (h *Hasher) Sum(b []byte) (s []byte) { 300 t := h.getTree() 301 // write the last section with final flag set to true 302 go h.writeSection(t.cursor, t.section, true, true) 303 // wait for the result 304 s = <-t.result 305 span := t.span 306 // release the tree resource back to the pool 307 h.releaseTree() 308 // b + sha3(span + BMT(pure_chunk)) 309 if len(span) == 0 { 310 return append(b, s...) 311 } 312 return doSum(h.pool.hasher(), b, span, s) 313 } 314 315 // methods needed to implement the SwarmHash and the io.Writer interfaces 316 317 // Write calls sequentially add to the buffer to be hashed, 318 // with every full segment calls writeSection in a go routine 319 func (h *Hasher) Write(b []byte) (int, error) { 320 l := len(b) 321 if l == 0 || l > 4096 { 322 return 0, nil 323 } 324 t := h.getTree() 325 secsize := 2 * h.pool.SegmentSize 326 // calculate length of missing bit to complete current open section 327 smax := secsize - t.offset 328 // if at the beginning of chunk or middle of the section 329 if t.offset < secsize { 330 // fill up current segment from buffer 331 copy(t.section[t.offset:], b) 332 // if input buffer consumed and open section not complete, then 333 // advance offset and return 334 if smax == 0 { 335 smax = secsize 336 } 337 if l <= smax { 338 t.offset += l 339 return l, nil 340 } 341 } else { 342 // if end of a section 343 if t.cursor == h.pool.SegmentCount*2 { 344 return 0, nil 345 } 346 } 347 // read full sections and the last possibly partial section from the input buffer 348 for smax < l { 349 // section complete; push to tree asynchronously 350 go h.writeSection(t.cursor, t.section, true, false) 351 // reset section 352 t.section = make([]byte, secsize) 353 // copy from input buffer at smax to right half of section 354 copy(t.section, b[smax:]) 355 // advance cursor 356 t.cursor++ 357 // smax here represents successive offsets in the input buffer 358 smax += secsize 359 } 360 t.offset = l - smax + secsize 361 return l, nil 362 } 363 364 // Reset needs to be called before writing to the hasher 365 func (h *Hasher) Reset() { 366 h.releaseTree() 367 } 368 369 // methods needed to implement the SwarmHash interface 370 371 // ResetWithLength needs to be called before writing to the hasher 372 // the argument is supposed to be the byte slice binary representation of 373 // the length of the data subsumed under the hash, i.e., span 374 func (h *Hasher) ResetWithLength(span []byte) { 375 h.Reset() 376 h.getTree().span = span 377 } 378 379 // releaseTree gives back the Tree to the pool whereby it unlocks 380 // it resets tree, segment and index 381 func (h *Hasher) releaseTree() { 382 t := h.bmt 383 if t == nil { 384 return 385 } 386 h.bmt = nil 387 go func() { 388 t.cursor = 0 389 t.offset = 0 390 t.span = nil 391 t.section = make([]byte, h.pool.SegmentSize*2) 392 select { 393 case <-t.result: 394 default: 395 } 396 h.pool.release(t) 397 }() 398 } 399 400 // NewAsyncWriter extends Hasher with an interface for concurrent segment/section writes 401 func (h *Hasher) NewAsyncWriter(double bool) *AsyncHasher { 402 secsize := h.pool.SegmentSize 403 if double { 404 secsize *= 2 405 } 406 write := func(i int, section []byte, final bool) { 407 h.writeSection(i, section, double, final) 408 } 409 return &AsyncHasher{ 410 Hasher: h, 411 double: double, 412 secsize: secsize, 413 write: write, 414 } 415 } 416 417 // SectionWriter is an asynchronous segment/section writer interface 418 type SectionWriter interface { 419 Reset() // standard init to be called before reuse 420 Write(index int, data []byte) // write into section of index 421 Sum(b []byte, length int, span []byte) []byte // returns the hash of the buffer 422 SectionSize() int // size of the async section unit to use 423 } 424 425 // AsyncHasher extends BMT Hasher with an asynchronous segment/section writer interface 426 // AsyncHasher is unsafe and does not check indexes and section data lengths 427 // it must be used with the right indexes and length and the right number of sections 428 // 429 // behaviour is undefined if 430 // * non-final sections are shorter or longer than secsize 431 // * if final section does not match length 432 // * write a section with index that is higher than length/secsize 433 // * set length in Sum call when length/secsize < maxsec 434 // 435 // * if Sum() is not called on a Hasher that is fully written 436 // a process will block, can be terminated with Reset 437 // * it will not leak processes if not all sections are written but it blocks 438 // and keeps the resource which can be released calling Reset() 439 type AsyncHasher struct { 440 *Hasher // extends the Hasher 441 mtx sync.Mutex // to lock the cursor access 442 double bool // whether to use double segments (call Hasher.writeSection) 443 secsize int // size of base section (size of hash or double) 444 write func(i int, section []byte, final bool) 445 } 446 447 // methods needed to implement AsyncWriter 448 449 // SectionSize returns the size of async section unit to use 450 func (sw *AsyncHasher) SectionSize() int { 451 return sw.secsize 452 } 453 454 // Write writes the i-th section of the BMT base 455 // this function can and is meant to be called concurrently 456 // it sets max segment threadsafely 457 func (sw *AsyncHasher) Write(i int, section []byte) { 458 sw.mtx.Lock() 459 defer sw.mtx.Unlock() 460 t := sw.getTree() 461 // cursor keeps track of the rightmost section written so far 462 // if index is lower than cursor then just write non-final section as is 463 if i < t.cursor { 464 // if index is not the rightmost, safe to write section 465 go sw.write(i, section, false) 466 return 467 } 468 // if there is a previous rightmost section safe to write section 469 if t.offset > 0 { 470 if i == t.cursor { 471 // i==cursor implies cursor was set by Hash call so we can write section as final one 472 // since it can be shorter, first we copy it to the padded buffer 473 t.section = make([]byte, sw.secsize) 474 copy(t.section, section) 475 go sw.write(i, t.section, true) 476 return 477 } 478 // the rightmost section just changed, so we write the previous one as non-final 479 go sw.write(t.cursor, t.section, false) 480 } 481 // set i as the index of the righmost section written so far 482 // set t.offset to cursor*secsize+1 483 t.cursor = i 484 t.offset = i*sw.secsize + 1 485 t.section = make([]byte, sw.secsize) 486 copy(t.section, section) 487 } 488 489 // Sum can be called any time once the length and the span is known 490 // potentially even before all segments have been written 491 // in such cases Sum will block until all segments are present and 492 // the hash for the length can be calculated. 493 // 494 // b: digest is appended to b 495 // length: known length of the input (unsafe; undefined if out of range) 496 // meta: metadata to hash together with BMT root for the final digest 497 // e.g., span for protection against existential forgery 498 func (sw *AsyncHasher) Sum(b []byte, length int, meta []byte) (s []byte) { 499 sw.mtx.Lock() 500 t := sw.getTree() 501 if length == 0 { 502 sw.mtx.Unlock() 503 s = sw.pool.zerohashes[sw.pool.Depth] 504 } else { 505 // for non-zero input the rightmost section is written to the tree asynchronously 506 // if the actual last section has been written (t.cursor == length/t.secsize) 507 maxsec := (length - 1) / sw.secsize 508 if t.offset > 0 { 509 go sw.write(t.cursor, t.section, maxsec == t.cursor) 510 } 511 // set cursor to maxsec so final section is written when it arrives 512 t.cursor = maxsec 513 t.offset = length 514 result := t.result 515 sw.mtx.Unlock() 516 // wait for the result or reset 517 s = <-result 518 } 519 // relesase the tree back to the pool 520 sw.releaseTree() 521 // if no meta is given just append digest to b 522 if len(meta) == 0 { 523 return append(b, s...) 524 } 525 // hash together meta and BMT root hash using the pools 526 return doSum(sw.pool.hasher(), b, meta, s) 527 } 528 529 // writeSection writes the hash of i-th section into level 1 node of the BMT tree 530 func (h *Hasher) writeSection(i int, section []byte, double bool, final bool) { 531 // select the leaf node for the section 532 var n *node 533 var isLeft bool 534 var hasher hash.Hash 535 var level int 536 t := h.getTree() 537 if double { 538 level++ 539 n = t.leaves[i] 540 hasher = n.hasher 541 isLeft = n.isLeft 542 n = n.parent 543 // hash the section 544 section = doSum(hasher, nil, section) 545 } else { 546 n = t.leaves[i/2] 547 hasher = n.hasher 548 isLeft = i%2 == 0 549 } 550 // write hash into parent node 551 if final { 552 // for the last segment use writeFinalNode 553 h.writeFinalNode(level, n, hasher, isLeft, section) 554 } else { 555 h.writeNode(n, hasher, isLeft, section) 556 } 557 } 558 559 // writeNode pushes the data to the node 560 // if it is the first of 2 sisters written, the routine terminates 561 // if it is the second, it calculates the hash and writes it 562 // to the parent node recursively 563 // since hashing the parent is synchronous the same hasher can be used 564 func (h *Hasher) writeNode(n *node, bh hash.Hash, isLeft bool, s []byte) { 565 level := 1 566 for { 567 // at the root of the bmt just write the result to the result channel 568 if n == nil { 569 h.getTree().result <- s 570 return 571 } 572 // otherwise assign child hash to left or right segment 573 if isLeft { 574 n.left = s 575 } else { 576 n.right = s 577 } 578 // the child-thread first arriving will terminate 579 if n.toggle() { 580 return 581 } 582 // the thread coming second now can be sure both left and right children are written 583 // so it calculates the hash of left|right and pushes it to the parent 584 s = doSum(bh, nil, n.left, n.right) 585 isLeft = n.isLeft 586 n = n.parent 587 level++ 588 } 589 } 590 591 // writeFinalNode is following the path starting from the final datasegment to the 592 // BMT root via parents 593 // for unbalanced trees it fills in the missing right sister nodes using 594 // the pool's lookup table for BMT subtree root hashes for all-zero sections 595 // otherwise behaves like `writeNode` 596 func (h *Hasher) writeFinalNode(level int, n *node, bh hash.Hash, isLeft bool, s []byte) { 597 598 for { 599 // at the root of the bmt just write the result to the result channel 600 if n == nil { 601 if s != nil { 602 h.getTree().result <- s 603 } 604 return 605 } 606 var noHash bool 607 if isLeft { 608 // coming from left sister branch 609 // when the final section's path is going via left child node 610 // we include an all-zero subtree hash for the right level and toggle the node. 611 n.right = h.pool.zerohashes[level] 612 if s != nil { 613 n.left = s 614 // if a left final node carries a hash, it must be the first (and only thread) 615 // so the toggle is already in passive state no need no call 616 // yet thread needs to carry on pushing hash to parent 617 noHash = false 618 } else { 619 // if again first thread then propagate nil and calculate no hash 620 noHash = n.toggle() 621 } 622 } else { 623 // right sister branch 624 if s != nil { 625 // if hash was pushed from right child node, write right segment change state 626 n.right = s 627 // if toggle is true, we arrived first so no hashing just push nil to parent 628 noHash = n.toggle() 629 630 } else { 631 // if s is nil, then thread arrived first at previous node and here there will be two, 632 // so no need to do anything and keep s = nil for parent 633 noHash = true 634 } 635 } 636 // the child-thread first arriving will just continue resetting s to nil 637 // the second thread now can be sure both left and right children are written 638 // it calculates the hash of left|right and pushes it to the parent 639 if noHash { 640 s = nil 641 } else { 642 s = doSum(bh, nil, n.left, n.right) 643 } 644 // iterate to parent 645 isLeft = n.isLeft 646 n = n.parent 647 level++ 648 } 649 } 650 651 // getTree obtains a BMT resource by reserving one from the pool and assigns it to the bmt field 652 func (h *Hasher) getTree() *tree { 653 if h.bmt != nil { 654 return h.bmt 655 } 656 t := h.pool.reserve() 657 h.bmt = t 658 return t 659 } 660 661 // atomic bool toggle implementing a concurrent reusable 2-state object 662 // atomic addint with %2 implements atomic bool toggle 663 // it returns true if the toggler just put it in the active/waiting state 664 func (n *node) toggle() bool { 665 return atomic.AddInt32(&n.state, 1)%2 == 1 666 } 667 668 // calculates the hash of the data using hash.Hash 669 func doSum(h hash.Hash, b []byte, data ...[]byte) []byte { 670 h.Reset() 671 for _, v := range data { 672 h.Write(v) 673 } 674 return h.Sum(b) 675 } 676 677 // hashstr is a pretty printer for bytes used in tree.draw 678 func hashstr(b []byte) string { 679 end := len(b) 680 if end > 4 { 681 end = 4 682 } 683 return fmt.Sprintf("%x", b[:end]) 684 } 685 686 // calculateDepthFor calculates the depth (number of levels) in the BMT tree 687 func calculateDepthFor(n int) (d int) { 688 c := 2 689 for ; c < n; c *= 2 { 690 d++ 691 } 692 return d + 1 693 }