Skip to content
This repository was archived by the owner on Aug 2, 2021. It is now read-only.

Commit ef67a8f

Browse files
zelignonsense
authored andcommitted
swarm/bmt: fix io.Writer interface
* Write now tolerates arbitrary variable buffers * added variable buffer tests * Write loop and finalise optimisation * refactor / rename * add tests for empty input
1 parent 2bbeccc commit ef67a8f

File tree

3 files changed

+220
-125
lines changed

3 files changed

+220
-125
lines changed

swarm/bmt/bmt.go

Lines changed: 121 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,7 @@ func NewTreePool(hasher BaseHasherFunc, segmentCount, capacity int) *TreePool {
117117
zerohashes[0] = zeros
118118
h := hasher()
119119
for i := 1; i < depth; i++ {
120-
h.Reset()
121-
h.Write(zeros)
122-
h.Write(zeros)
123-
zeros = h.Sum(nil)
120+
zeros = doHash(h, nil, zeros, zeros)
124121
zerohashes[i] = zeros
125122
}
126123
return &TreePool{
@@ -318,41 +315,19 @@ func (h *Hasher) Sum(b []byte) (r []byte) {
318315
// * if sequential write is used (can read sections)
319316
func (h *Hasher) sum(b []byte, release, section bool) (r []byte) {
320317
t := h.bmt
321-
h.finalise(section)
322-
if t.offset > 0 { // get the last node (double segment)
323-
324-
// padding the segment with zero
325-
copy(t.segment[t.offset:], h.pool.zerohashes[0])
326-
}
327-
if section {
328-
if t.cur%2 == 1 {
329-
// if just finished current segment, copy it to the right half of the chunk
330-
copy(t.section[h.pool.SegmentSize:], t.segment)
331-
} else {
332-
// copy segment to front of section, zero pad the right half
333-
copy(t.section, t.segment)
334-
copy(t.section[h.pool.SegmentSize:], h.pool.zerohashes[0])
335-
}
336-
h.writeSection(t.cur, t.section)
337-
} else {
338-
// TODO: h.writeSegment(t.cur, t.segment)
339-
panic("SegmentWriter not implemented")
340-
}
318+
bh := h.pool.hasher()
319+
go h.writeSection(t.cur, t.section, true)
341320
bmtHash := <-t.result
342321
span := t.span
343-
322+
// fmt.Println(t.draw(bmtHash))
344323
if release {
345324
h.releaseTree()
346325
}
347-
// sha3(span + BMT(pure_chunk))
326+
// b + sha3(span + BMT(pure_chunk))
348327
if span == nil {
349-
return bmtHash
328+
return append(b, bmtHash...)
350329
}
351-
bh := h.pool.hasher()
352-
bh.Reset()
353-
bh.Write(span)
354-
bh.Write(bmtHash)
355-
return bh.Sum(b)
330+
return doHash(bh, b, span, bmtHash)
356331
}
357332

358333
// Hasher implements the SwarmHash interface
@@ -367,37 +342,41 @@ func (h *Hasher) Write(b []byte) (int, error) {
367342
return 0, nil
368343
}
369344
t := h.bmt
370-
need := (h.pool.SegmentCount - t.cur) * h.pool.SegmentSize
371-
if l < need {
372-
need = l
373-
}
374-
// calculate missing bit to complete current open segment
375-
rest := h.pool.SegmentSize - t.offset
376-
if need < rest {
377-
rest = need
378-
}
379-
copy(t.segment[t.offset:], b[:rest])
380-
need -= rest
381-
size := (t.offset + rest) % h.pool.SegmentSize
382-
// read full segments and the last possibly partial segment
383-
for need > 0 {
384-
// push all finished chunks we read
385-
if t.cur%2 == 0 {
386-
copy(t.section, t.segment)
387-
} else {
388-
copy(t.section[h.pool.SegmentSize:], t.segment)
389-
h.writeSection(t.cur, t.section)
345+
secsize := 2 * h.pool.SegmentSize
346+
// calculate length of missing bit to complete current open section
347+
smax := secsize - t.offset
348+
// if at the beginning of chunk or middle of the section
349+
if t.offset < secsize {
350+
// fill up current segment from buffer
351+
copy(t.section[t.offset:], b)
352+
// if input buffer consumed and open section not complete, then
353+
// advance offset and return
354+
if smax == 0 {
355+
smax = secsize
356+
}
357+
if l <= smax {
358+
t.offset += l
359+
return l, nil
390360
}
391-
size = h.pool.SegmentSize
392-
if need < size {
393-
size = need
361+
} else {
362+
if t.cur == h.pool.SegmentCount*2 {
363+
return 0, nil
394364
}
395-
copy(t.segment, b[rest:rest+size])
396-
need -= size
397-
rest += size
365+
}
366+
// read full segments and the last possibly partial segment from the input buffer
367+
for smax < l {
368+
// section complete; push to tree asynchronously
369+
go h.writeSection(t.cur, t.section, false)
370+
// reset section
371+
t.section = make([]byte, secsize)
372+
// copy from imput buffer at smax to right half of section
373+
copy(t.section, b[smax:])
374+
// advance cursor
398375
t.cur++
376+
// smax here represents successive offsets in the input buffer
377+
smax += secsize
399378
}
400-
t.offset = size % h.pool.SegmentSize
379+
t.offset = l - smax + secsize
401380
return l, nil
402381
}
403382

@@ -426,6 +405,8 @@ func (h *Hasher) releaseTree() {
426405
t.span = nil
427406
t.hash = nil
428407
h.bmt = nil
408+
t.section = make([]byte, h.pool.SegmentSize*2)
409+
t.segment = make([]byte, h.pool.SegmentSize)
429410
h.pool.release(t)
430411
}
431412
}
@@ -435,29 +416,37 @@ func (h *Hasher) releaseTree() {
435416
// go h.run(h.bmt.leaves[i/2], h.pool.hasher(), i%2 == 0, s)
436417
// }
437418

438-
// writeSection writes the hash of i/2-th segction into right level 1 node of the BMT tree
439-
func (h *Hasher) writeSection(i int, section []byte) {
440-
n := h.bmt.leaves[i/2]
419+
// writeSection writes the hash of i-th section into level 1 node of the BMT tree
420+
func (h *Hasher) writeSection(i int, section []byte, final bool) {
421+
// select the leaf node for the section
422+
n := h.bmt.leaves[i]
441423
isLeft := n.isLeft
442424
n = n.parent
443425
bh := h.pool.hasher()
444-
bh.Write(section)
445-
go func() {
446-
sum := bh.Sum(nil)
447-
if n == nil {
448-
h.bmt.result <- sum
449-
return
450-
}
451-
h.run(n, bh, isLeft, sum)
452-
}()
426+
// hash the section
427+
s := doHash(bh, nil, section)
428+
// write hash into parent node
429+
if final {
430+
// for the last segment use writeFinalNode
431+
h.writeFinalNode(1, n, bh, isLeft, s)
432+
} else {
433+
h.writeNode(n, bh, isLeft, s)
434+
}
453435
}
454436

455-
// run pushes the data to the node
437+
// writeNode pushes the data to the node
456438
// if it is the first of 2 sisters written the routine returns
457439
// if it is the second, it calculates the hash and writes it
458440
// to the parent node recursively
459-
func (h *Hasher) run(n *node, bh hash.Hash, isLeft bool, s []byte) {
441+
func (h *Hasher) writeNode(n *node, bh hash.Hash, isLeft bool, s []byte) {
442+
level := 1
460443
for {
444+
// at the root of the bmt just write the result to the result channel
445+
if n == nil {
446+
h.bmt.result <- s
447+
return
448+
}
449+
// otherwise assign child hash to branc
461450
if isLeft {
462451
n.left = s
463452
} else {
@@ -467,44 +456,68 @@ func (h *Hasher) run(n *node, bh hash.Hash, isLeft bool, s []byte) {
467456
if n.toggle() {
468457
return
469458
}
470-
// the second thread now can be sure both left and right children are written
471-
// it calculates the hash of left|right and take it to the next level
472-
bh.Reset()
473-
bh.Write(n.left)
474-
bh.Write(n.right)
475-
s = bh.Sum(nil)
476-
477-
// at the root of the bmt just write the result to the result channel
478-
if n.parent == nil {
479-
h.bmt.result <- s
480-
return
481-
}
482-
483-
// otherwise iterate on parent
459+
// the thread coming later now can be sure both left and right children are written
460+
// it calculates the hash of left|right and pushes it to the parent
461+
s = doHash(bh, nil, n.left, n.right)
484462
isLeft = n.isLeft
485463
n = n.parent
464+
level++
486465
}
487466
}
488467

489-
// finalise is following the path starting from the final datasegment to the
468+
// writeFinalNode is following the path starting from the final datasegment to the
490469
// BMT root via parents
491470
// for unbalanced trees it fills in the missing right sister nodes using
492471
// the pool's lookup table for BMT subtree root hashes for all-zero sections
493-
func (h *Hasher) finalise(skip bool) {
494-
t := h.bmt
495-
isLeft := t.cur%2 == 0
496-
n := t.leaves[t.cur/2]
497-
for level := 0; n != nil; level++ {
498-
// when the final segment's path is going via left child node
499-
// we include an all-zero subtree hash for the right level and toggle the node.
500-
// when the path is going through right child node, nothing to do
501-
if isLeft && !skip {
472+
// otherwise behaves like `writeNode`
473+
func (h *Hasher) writeFinalNode(level int, n *node, bh hash.Hash, isLeft bool, s []byte) {
474+
475+
for {
476+
// at the root of the bmt just write the result to the result channel
477+
if n == nil {
478+
if s != nil {
479+
h.bmt.result <- s
480+
}
481+
return
482+
}
483+
var noHash bool
484+
if isLeft {
485+
// coming from left sister branch
486+
// when the final section's path is going via left child node
487+
// we include an all-zero subtree hash for the right level and toggle the node.
488+
// when the path is going through right child node, nothing to do
502489
n.right = h.pool.zerohashes[level]
503-
n.toggle()
490+
if s != nil {
491+
n.left = s
492+
// if a left final node carries a hash, it must be the first (and only thread)
493+
// so the toggle is already in passive state no need no call
494+
// yet thread needs to carry on pushing hash to parent
495+
} else {
496+
// if again first thread then propagate nil and calculate no hash
497+
noHash = n.toggle()
498+
}
499+
} else {
500+
// right sister branch
501+
// if s is nil, then thread arrived first at previous node and here there will be two,
502+
// so no need to do anything
503+
if s != nil {
504+
n.right = s
505+
noHash = n.toggle()
506+
} else {
507+
noHash = true
508+
}
509+
}
510+
// the child-thread first arriving will just continue resetting s to nil
511+
// the second thread now can be sure both left and right children are written
512+
// it calculates the hash of left|right and pushes it to the parent
513+
if noHash {
514+
s = nil
515+
} else {
516+
s = doHash(bh, nil, n.left, n.right)
504517
}
505-
skip = false
506518
isLeft = n.isLeft
507519
n = n.parent
520+
level++
508521
}
509522
}
510523

@@ -525,6 +538,15 @@ func (n *node) toggle() bool {
525538
return atomic.AddInt32(&n.state, 1)%2 == 1
526539
}
527540

541+
// calculates the hash of the data using hash.Hash
542+
func doHash(h hash.Hash, b []byte, data ...[]byte) []byte {
543+
h.Reset()
544+
for _, v := range data {
545+
h.Write(v)
546+
}
547+
return h.Sum(b)
548+
}
549+
528550
func hashstr(b []byte) string {
529551
end := len(b)
530552
if end > 4 {

swarm/bmt/bmt_r.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,5 @@ func (rh *RefHasher) hash(data []byte, length int) []byte {
8080
}
8181
rh.hasher.Reset()
8282
rh.hasher.Write(section)
83-
s := rh.hasher.Sum(nil)
84-
return s
83+
return rh.hasher.Sum(nil)
8584
}

0 commit comments

Comments
 (0)