| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // Copyright 2018 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // Package bmt provides a binary merkle tree implementation used for swarm chunk hash | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | package bmt | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"hash" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"sync/atomic" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | Binary Merkle Tree Hash is a hash function over arbitrary datachunks of limited size. | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | It is defined as the root hash of the binary merkle tree built over fixed size segments | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | of the underlying chunk using any base hash function (e.g., keccak 256 SHA3). | 
					
						
							|  |  |  | Chunks with data shorter than the fixed size are hashed as if they had zero padding. | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | BMT hash is used as the chunk hash function in swarm which in turn is the basis for the | 
					
						
							|  |  |  | 128 branching swarm hash http://swarm-guide.readthedocs.io/en/latest/architecture.html#swarm-hash | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The BMT is optimal for providing compact inclusion proofs, i.e. prove that a | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | segment is a substring of a chunk starting at a particular offset. | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | The size of the underlying segments is fixed to the size of the base hash (called the resolution | 
					
						
							|  |  |  | of the BMT hash), Using Keccak256 SHA3 hash is 32 bytes, the EVM word size to optimize for on-chain BMT verification | 
					
						
							|  |  |  | as well as the hash size optimal for inclusion proofs in the merkle tree of the swarm hash. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Two implementations are provided: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | * RefHasher is optimized for code simplicity and meant as a reference implementation | 
					
						
							|  |  |  |   that is simple to understand | 
					
						
							|  |  |  | * Hasher is optimized for speed taking advantage of concurrency with minimalistic | 
					
						
							|  |  |  |   control structure to coordinate the concurrent routines | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   BMT Hasher implements the following interfaces | 
					
						
							|  |  |  | 	* standard golang hash.Hash - synchronous, reusable | 
					
						
							|  |  |  | 	* SwarmHash - SumWithSpan provided | 
					
						
							|  |  |  | 	* io.Writer - synchronous left-to-right datawriter | 
					
						
							|  |  |  | 	* AsyncWriter - concurrent section writes and asynchronous Sum call | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// PoolSize is the maximum number of bmt trees used by the hashers, i.e, | 
					
						
							|  |  |  | 	// the maximum number of concurrent BMT hashing operations performed by the same hasher | 
					
						
							|  |  |  | 	PoolSize = 8 | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // BaseHasherFunc is a hash.Hash constructor function used for the base hash of the BMT. | 
					
						
							| 
									
										
										
										
											2019-01-03 16:15:26 -06:00
										 |  |  | // implemented by Keccak256 SHA3 sha3.NewLegacyKeccak256 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | type BaseHasherFunc func() hash.Hash | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Hasher a reusable hasher for fixed maximum size chunks representing a BMT | 
					
						
							|  |  |  | // - implements the hash.Hash interface | 
					
						
							|  |  |  | // - reuses a pool of trees for amortised memory allocation and resource control | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // - supports order-agnostic concurrent segment writes and section (double segment) writes | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | //   as well as sequential read and write | 
					
						
							|  |  |  | // - the same hasher instance must not be called concurrently on more than one chunk | 
					
						
							|  |  |  | // - the same hasher instance is synchronously reuseable | 
					
						
							|  |  |  | // - Sum gives back the tree to the pool and guaranteed to leave | 
					
						
							|  |  |  | //   the tree and itself in a state reusable for hashing a new chunk | 
					
						
							|  |  |  | // - generates and verifies segment inclusion proofs (TODO:) | 
					
						
							|  |  |  | type Hasher struct { | 
					
						
							|  |  |  | 	pool *TreePool // BMT resource pool | 
					
						
							|  |  |  | 	bmt  *tree     // prebuilt BMT resource for flowcontrol and proofs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // New creates a reusable BMT Hasher that | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // pulls a new tree from a resource pool for hashing each chunk | 
					
						
							|  |  |  | func New(p *TreePool) *Hasher { | 
					
						
							|  |  |  | 	return &Hasher{ | 
					
						
							|  |  |  | 		pool: p, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // TreePool provides a pool of trees used as resources by the BMT Hasher. | 
					
						
							|  |  |  | // A tree popped from the pool is guaranteed to have a clean state ready | 
					
						
							|  |  |  | // for hashing a new chunk. | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | type TreePool struct { | 
					
						
							|  |  |  | 	lock         sync.Mutex | 
					
						
							|  |  |  | 	c            chan *tree     // the channel to obtain a resource from the pool | 
					
						
							|  |  |  | 	hasher       BaseHasherFunc // base hasher to use for the BMT levels | 
					
						
							|  |  |  | 	SegmentSize  int            // size of leaf segments, stipulated to be = hash size | 
					
						
							|  |  |  | 	SegmentCount int            // the number of segments on the base level of the BMT | 
					
						
							|  |  |  | 	Capacity     int            // pool capacity, controls concurrency | 
					
						
							|  |  |  | 	Depth        int            // depth of the bmt trees = int(log2(segmentCount))+1 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	Size         int            // the total length of the data (count * size) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	count        int            // current count of (ever) allocated resources | 
					
						
							|  |  |  | 	zerohashes   [][]byte       // lookup table for predictable padding subtrees for all levels | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewTreePool creates a tree pool with hasher, segment size, segment count and capacity | 
					
						
							|  |  |  | // on Hasher.getTree it reuses free trees or creates a new one if capacity is not reached | 
					
						
							|  |  |  | func NewTreePool(hasher BaseHasherFunc, segmentCount, capacity int) *TreePool { | 
					
						
							|  |  |  | 	// initialises the zerohashes lookup table | 
					
						
							|  |  |  | 	depth := calculateDepthFor(segmentCount) | 
					
						
							|  |  |  | 	segmentSize := hasher().Size() | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	zerohashes := make([][]byte, depth+1) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	zeros := make([]byte, segmentSize) | 
					
						
							|  |  |  | 	zerohashes[0] = zeros | 
					
						
							|  |  |  | 	h := hasher() | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	for i := 1; i < depth+1; i++ { | 
					
						
							|  |  |  | 		zeros = doSum(h, nil, zeros, zeros) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		zerohashes[i] = zeros | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return &TreePool{ | 
					
						
							|  |  |  | 		c:            make(chan *tree, capacity), | 
					
						
							|  |  |  | 		hasher:       hasher, | 
					
						
							|  |  |  | 		SegmentSize:  segmentSize, | 
					
						
							|  |  |  | 		SegmentCount: segmentCount, | 
					
						
							|  |  |  | 		Capacity:     capacity, | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		Size:         segmentCount * segmentSize, | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		Depth:        depth, | 
					
						
							|  |  |  | 		zerohashes:   zerohashes, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Drain drains the pool until it has no more than n resources | 
					
						
							|  |  |  | func (p *TreePool) Drain(n int) { | 
					
						
							|  |  |  | 	p.lock.Lock() | 
					
						
							|  |  |  | 	defer p.lock.Unlock() | 
					
						
							|  |  |  | 	for len(p.c) > n { | 
					
						
							|  |  |  | 		<-p.c | 
					
						
							|  |  |  | 		p.count-- | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Reserve is blocking until it returns an available tree | 
					
						
							|  |  |  | // it reuses free trees or creates a new one if size is not reached | 
					
						
							|  |  |  | // TODO: should use a context here | 
					
						
							|  |  |  | func (p *TreePool) reserve() *tree { | 
					
						
							|  |  |  | 	p.lock.Lock() | 
					
						
							|  |  |  | 	defer p.lock.Unlock() | 
					
						
							|  |  |  | 	var t *tree | 
					
						
							|  |  |  | 	if p.count == p.Capacity { | 
					
						
							|  |  |  | 		return <-p.c | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case t = <-p.c: | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		t = newTree(p.SegmentSize, p.Depth, p.hasher) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		p.count++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return t | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // release gives back a tree to the pool. | 
					
						
							|  |  |  | // this tree is guaranteed to be in reusable state | 
					
						
							|  |  |  | func (p *TreePool) release(t *tree) { | 
					
						
							|  |  |  | 	p.c <- t // can never fail ... | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // tree is a reusable control structure representing a BMT | 
					
						
							|  |  |  | // organised in a binary tree | 
					
						
							|  |  |  | // Hasher uses a TreePool to obtain a tree for each chunk hash | 
					
						
							|  |  |  | // the tree is 'locked' while not in the pool | 
					
						
							|  |  |  | type tree struct { | 
					
						
							|  |  |  | 	leaves  []*node     // leaf nodes of the tree, other nodes accessible via parent links | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	cursor  int         // index of rightmost currently open segment | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	offset  int         // offset (cursor position) within currently open segment | 
					
						
							|  |  |  | 	section []byte      // the rightmost open section (double segment) | 
					
						
							|  |  |  | 	result  chan []byte // result channel | 
					
						
							|  |  |  | 	span    []byte      // The span of the data subsumed under the chunk | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // node is a reuseable segment hasher representing a node in a BMT | 
					
						
							|  |  |  | type node struct { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	isLeft      bool      // whether it is left side of the parent double segment | 
					
						
							|  |  |  | 	parent      *node     // pointer to parent node in the BMT | 
					
						
							|  |  |  | 	state       int32     // atomic increment impl concurrent boolean toggle | 
					
						
							|  |  |  | 	left, right []byte    // this is where the two children sections are written | 
					
						
							|  |  |  | 	hasher      hash.Hash // preconstructed hasher on nodes | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newNode constructs a segment hasher node in the BMT (used by newTree) | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | func newNode(index int, parent *node, hasher hash.Hash) *node { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	return &node{ | 
					
						
							|  |  |  | 		parent: parent, | 
					
						
							|  |  |  | 		isLeft: index%2 == 0, | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		hasher: hasher, | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Draw draws the BMT (badly) | 
					
						
							|  |  |  | func (t *tree) draw(hash []byte) string { | 
					
						
							|  |  |  | 	var left, right []string | 
					
						
							|  |  |  | 	var anc []*node | 
					
						
							|  |  |  | 	for i, n := range t.leaves { | 
					
						
							|  |  |  | 		left = append(left, fmt.Sprintf("%v", hashstr(n.left))) | 
					
						
							|  |  |  | 		if i%2 == 0 { | 
					
						
							|  |  |  | 			anc = append(anc, n.parent) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		right = append(right, fmt.Sprintf("%v", hashstr(n.right))) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	anc = t.leaves | 
					
						
							|  |  |  | 	var hashes [][]string | 
					
						
							|  |  |  | 	for l := 0; len(anc) > 0; l++ { | 
					
						
							|  |  |  | 		var nodes []*node | 
					
						
							|  |  |  | 		hash := []string{""} | 
					
						
							|  |  |  | 		for i, n := range anc { | 
					
						
							|  |  |  | 			hash = append(hash, fmt.Sprintf("%v|%v", hashstr(n.left), hashstr(n.right))) | 
					
						
							|  |  |  | 			if i%2 == 0 && n.parent != nil { | 
					
						
							|  |  |  | 				nodes = append(nodes, n.parent) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		hash = append(hash, "") | 
					
						
							|  |  |  | 		hashes = append(hashes, hash) | 
					
						
							|  |  |  | 		anc = nodes | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	hashes = append(hashes, []string{"", fmt.Sprintf("%v", hashstr(hash)), ""}) | 
					
						
							|  |  |  | 	total := 60 | 
					
						
							|  |  |  | 	del := "                             " | 
					
						
							|  |  |  | 	var rows []string | 
					
						
							|  |  |  | 	for i := len(hashes) - 1; i >= 0; i-- { | 
					
						
							|  |  |  | 		var textlen int | 
					
						
							|  |  |  | 		hash := hashes[i] | 
					
						
							|  |  |  | 		for _, s := range hash { | 
					
						
							|  |  |  | 			textlen += len(s) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if total < textlen { | 
					
						
							|  |  |  | 			total = textlen + len(hash) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		delsize := (total - textlen) / (len(hash) - 1) | 
					
						
							|  |  |  | 		if delsize > len(del) { | 
					
						
							|  |  |  | 			delsize = len(del) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		row := fmt.Sprintf("%v: %v", len(hashes)-i-1, strings.Join(hash, del[:delsize])) | 
					
						
							|  |  |  | 		rows = append(rows, row) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	rows = append(rows, strings.Join(left, "  ")) | 
					
						
							|  |  |  | 	rows = append(rows, strings.Join(right, "  ")) | 
					
						
							|  |  |  | 	return strings.Join(rows, "\n") + "\n" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // newTree initialises a tree by building up the nodes of a BMT | 
					
						
							|  |  |  | // - segment size is stipulated to be the size of the hash | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | func newTree(segmentSize, depth int, hashfunc func() hash.Hash) *tree { | 
					
						
							|  |  |  | 	n := newNode(0, nil, hashfunc()) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	prevlevel := []*node{n} | 
					
						
							|  |  |  | 	// iterate over levels and creates 2^(depth-level) nodes | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	// the 0 level is on double segment sections so we start at depth - 2 since | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	count := 2 | 
					
						
							|  |  |  | 	for level := depth - 2; level >= 0; level-- { | 
					
						
							|  |  |  | 		nodes := make([]*node, count) | 
					
						
							|  |  |  | 		for i := 0; i < count; i++ { | 
					
						
							|  |  |  | 			parent := prevlevel[i/2] | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 			var hasher hash.Hash | 
					
						
							|  |  |  | 			if level == 0 { | 
					
						
							|  |  |  | 				hasher = hashfunc() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			nodes[i] = newNode(i, parent, hasher) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		prevlevel = nodes | 
					
						
							|  |  |  | 		count *= 2 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// the datanode level is the nodes on the last level | 
					
						
							|  |  |  | 	return &tree{ | 
					
						
							|  |  |  | 		leaves:  prevlevel, | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		result:  make(chan []byte), | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		section: make([]byte, 2*segmentSize), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // methods needed to implement hash.Hash | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Size returns the size | 
					
						
							|  |  |  | func (h *Hasher) Size() int { | 
					
						
							|  |  |  | 	return h.pool.SegmentSize | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // BlockSize returns the block size | 
					
						
							|  |  |  | func (h *Hasher) BlockSize() int { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	return 2 * h.pool.SegmentSize | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // Sum returns the BMT root hash of the buffer | 
					
						
							|  |  |  | // using Sum presupposes sequential synchronous writes (io.Writer interface) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // hash.Hash interface Sum method appends the byte slice to the underlying | 
					
						
							|  |  |  | // data before it calculates and returns the hash of the chunk | 
					
						
							|  |  |  | // caller must make sure Sum is not called concurrently with Write, writeSection | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | func (h *Hasher) Sum(b []byte) (s []byte) { | 
					
						
							|  |  |  | 	t := h.getTree() | 
					
						
							|  |  |  | 	// write the last section with final flag set to true | 
					
						
							|  |  |  | 	go h.writeSection(t.cursor, t.section, true, true) | 
					
						
							|  |  |  | 	// wait for the result | 
					
						
							|  |  |  | 	s = <-t.result | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	span := t.span | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	// release the tree resource back to the pool | 
					
						
							|  |  |  | 	h.releaseTree() | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	// b + sha3(span + BMT(pure_chunk)) | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	if len(span) == 0 { | 
					
						
							|  |  |  | 		return append(b, s...) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	return doSum(h.pool.hasher(), b, span, s) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // methods needed to implement the SwarmHash and the io.Writer interfaces | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // Write calls sequentially add to the buffer to be hashed, | 
					
						
							|  |  |  | // with every full segment calls writeSection in a go routine | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (h *Hasher) Write(b []byte) (int, error) { | 
					
						
							|  |  |  | 	l := len(b) | 
					
						
							| 
									
										
										
										
											2018-08-14 16:03:56 +02:00
										 |  |  | 	if l == 0 || l > h.pool.Size { | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		return 0, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	t := h.getTree() | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	secsize := 2 * h.pool.SegmentSize | 
					
						
							|  |  |  | 	// calculate length of missing bit to complete current open section | 
					
						
							|  |  |  | 	smax := secsize - t.offset | 
					
						
							|  |  |  | 	// if at the beginning of chunk or middle of the section | 
					
						
							|  |  |  | 	if t.offset < secsize { | 
					
						
							|  |  |  | 		// fill up current segment from buffer | 
					
						
							|  |  |  | 		copy(t.section[t.offset:], b) | 
					
						
							|  |  |  | 		// if input buffer consumed and open section not complete, then | 
					
						
							|  |  |  | 		// advance offset and return | 
					
						
							|  |  |  | 		if smax == 0 { | 
					
						
							|  |  |  | 			smax = secsize | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if l <= smax { | 
					
						
							|  |  |  | 			t.offset += l | 
					
						
							|  |  |  | 			return l, nil | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		// if end of a section | 
					
						
							|  |  |  | 		if t.cursor == h.pool.SegmentCount*2 { | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 			return 0, nil | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	// read full sections and the last possibly partial section from the input buffer | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	for smax < l { | 
					
						
							|  |  |  | 		// section complete; push to tree asynchronously | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		go h.writeSection(t.cursor, t.section, true, false) | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		// reset section | 
					
						
							|  |  |  | 		t.section = make([]byte, secsize) | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		// copy from input buffer at smax to right half of section | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		copy(t.section, b[smax:]) | 
					
						
							|  |  |  | 		// advance cursor | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		t.cursor++ | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		// smax here represents successive offsets in the input buffer | 
					
						
							|  |  |  | 		smax += secsize | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	t.offset = l - smax + secsize | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	return l, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Reset needs to be called before writing to the hasher | 
					
						
							|  |  |  | func (h *Hasher) Reset() { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	h.releaseTree() | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // methods needed to implement the SwarmHash interface | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ResetWithLength needs to be called before writing to the hasher | 
					
						
							|  |  |  | // the argument is supposed to be the byte slice binary representation of | 
					
						
							|  |  |  | // the length of the data subsumed under the hash, i.e., span | 
					
						
							|  |  |  | func (h *Hasher) ResetWithLength(span []byte) { | 
					
						
							|  |  |  | 	h.Reset() | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	h.getTree().span = span | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // releaseTree gives back the Tree to the pool whereby it unlocks | 
					
						
							|  |  |  | // it resets tree, segment and index | 
					
						
							|  |  |  | func (h *Hasher) releaseTree() { | 
					
						
							|  |  |  | 	t := h.bmt | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	if t == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	h.bmt = nil | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		t.cursor = 0 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		t.offset = 0 | 
					
						
							|  |  |  | 		t.span = nil | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		t.section = make([]byte, h.pool.SegmentSize*2) | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		select { | 
					
						
							|  |  |  | 		case <-t.result: | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		h.pool.release(t) | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	}() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewAsyncWriter extends Hasher with an interface for concurrent segment/section writes | 
					
						
							|  |  |  | func (h *Hasher) NewAsyncWriter(double bool) *AsyncHasher { | 
					
						
							|  |  |  | 	secsize := h.pool.SegmentSize | 
					
						
							|  |  |  | 	if double { | 
					
						
							|  |  |  | 		secsize *= 2 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	write := func(i int, section []byte, final bool) { | 
					
						
							|  |  |  | 		h.writeSection(i, section, double, final) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	return &AsyncHasher{ | 
					
						
							|  |  |  | 		Hasher:  h, | 
					
						
							|  |  |  | 		double:  double, | 
					
						
							|  |  |  | 		secsize: secsize, | 
					
						
							|  |  |  | 		write:   write, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SectionWriter is an asynchronous segment/section writer interface | 
					
						
							|  |  |  | type SectionWriter interface { | 
					
						
							|  |  |  | 	Reset()                                       // standard init to be called before reuse | 
					
						
							|  |  |  | 	Write(index int, data []byte)                 // write into section of index | 
					
						
							|  |  |  | 	Sum(b []byte, length int, span []byte) []byte // returns the hash of the buffer | 
					
						
							|  |  |  | 	SectionSize() int                             // size of the async section unit to use | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // AsyncHasher extends BMT Hasher with an asynchronous segment/section writer interface | 
					
						
							|  |  |  | // AsyncHasher is unsafe and does not check indexes and section data lengths | 
					
						
							|  |  |  | // it must be used with the right indexes and length and the right number of sections | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // behaviour is undefined if | 
					
						
							|  |  |  | // * non-final sections are shorter or longer than secsize | 
					
						
							|  |  |  | // * if final section does not match length | 
					
						
							|  |  |  | // * write a section with index that is higher than length/secsize | 
					
						
							|  |  |  | // * set length in Sum call when length/secsize < maxsec | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // * if Sum() is not called on a Hasher that is fully written | 
					
						
							|  |  |  | //   a process will block, can be terminated with Reset | 
					
						
							|  |  |  | // * it will not leak processes if not all sections are written but it blocks | 
					
						
							|  |  |  | //   and keeps the resource which can be released calling Reset() | 
					
						
							|  |  |  | type AsyncHasher struct { | 
					
						
							|  |  |  | 	*Hasher            // extends the Hasher | 
					
						
							|  |  |  | 	mtx     sync.Mutex // to lock the cursor access | 
					
						
							|  |  |  | 	double  bool       // whether to use double segments (call Hasher.writeSection) | 
					
						
							|  |  |  | 	secsize int        // size of base section (size of hash or double) | 
					
						
							|  |  |  | 	write   func(i int, section []byte, final bool) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // methods needed to implement AsyncWriter | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SectionSize returns the size of async section unit to use | 
					
						
							|  |  |  | func (sw *AsyncHasher) SectionSize() int { | 
					
						
							|  |  |  | 	return sw.secsize | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write writes the i-th section of the BMT base | 
					
						
							|  |  |  | // this function can and is meant to be called concurrently | 
					
						
							|  |  |  | // it sets max segment threadsafely | 
					
						
							|  |  |  | func (sw *AsyncHasher) Write(i int, section []byte) { | 
					
						
							|  |  |  | 	sw.mtx.Lock() | 
					
						
							|  |  |  | 	defer sw.mtx.Unlock() | 
					
						
							|  |  |  | 	t := sw.getTree() | 
					
						
							|  |  |  | 	// cursor keeps track of the rightmost section written so far | 
					
						
							|  |  |  | 	// if index is lower than cursor then just write non-final section as is | 
					
						
							|  |  |  | 	if i < t.cursor { | 
					
						
							|  |  |  | 		// if index is not the rightmost, safe to write section | 
					
						
							|  |  |  | 		go sw.write(i, section, false) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// if there is a previous rightmost section safe to write section | 
					
						
							|  |  |  | 	if t.offset > 0 { | 
					
						
							|  |  |  | 		if i == t.cursor { | 
					
						
							|  |  |  | 			// i==cursor implies cursor was set by Hash call so we can write section as final one | 
					
						
							|  |  |  | 			// since it can be shorter, first we copy it to the padded buffer | 
					
						
							|  |  |  | 			t.section = make([]byte, sw.secsize) | 
					
						
							|  |  |  | 			copy(t.section, section) | 
					
						
							|  |  |  | 			go sw.write(i, t.section, true) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// the rightmost section just changed, so we write the previous one as non-final | 
					
						
							|  |  |  | 		go sw.write(t.cursor, t.section, false) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// set i as the index of the righmost section written so far | 
					
						
							|  |  |  | 	// set t.offset to cursor*secsize+1 | 
					
						
							|  |  |  | 	t.cursor = i | 
					
						
							|  |  |  | 	t.offset = i*sw.secsize + 1 | 
					
						
							|  |  |  | 	t.section = make([]byte, sw.secsize) | 
					
						
							|  |  |  | 	copy(t.section, section) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Sum can be called any time once the length and the span is known | 
					
						
							|  |  |  | // potentially even before all segments have been written | 
					
						
							|  |  |  | // in such cases Sum will block until all segments are present and | 
					
						
							|  |  |  | // the hash for the length can be calculated. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // b: digest is appended to b | 
					
						
							|  |  |  | // length: known length of the input (unsafe; undefined if out of range) | 
					
						
							|  |  |  | // meta: metadata to hash together with BMT root for the final digest | 
					
						
							|  |  |  | //   e.g., span for protection against existential forgery | 
					
						
							|  |  |  | func (sw *AsyncHasher) Sum(b []byte, length int, meta []byte) (s []byte) { | 
					
						
							|  |  |  | 	sw.mtx.Lock() | 
					
						
							|  |  |  | 	t := sw.getTree() | 
					
						
							|  |  |  | 	if length == 0 { | 
					
						
							|  |  |  | 		sw.mtx.Unlock() | 
					
						
							|  |  |  | 		s = sw.pool.zerohashes[sw.pool.Depth] | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// for non-zero input the rightmost section is written to the tree asynchronously | 
					
						
							|  |  |  | 		// if the actual last section has been written (t.cursor == length/t.secsize) | 
					
						
							|  |  |  | 		maxsec := (length - 1) / sw.secsize | 
					
						
							|  |  |  | 		if t.offset > 0 { | 
					
						
							|  |  |  | 			go sw.write(t.cursor, t.section, maxsec == t.cursor) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// set cursor to maxsec so final section is written when it arrives | 
					
						
							|  |  |  | 		t.cursor = maxsec | 
					
						
							|  |  |  | 		t.offset = length | 
					
						
							|  |  |  | 		result := t.result | 
					
						
							|  |  |  | 		sw.mtx.Unlock() | 
					
						
							|  |  |  | 		// wait for the result or reset | 
					
						
							|  |  |  | 		s = <-result | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// relesase the tree back to the pool | 
					
						
							|  |  |  | 	sw.releaseTree() | 
					
						
							|  |  |  | 	// if no meta is given just append digest to b | 
					
						
							|  |  |  | 	if len(meta) == 0 { | 
					
						
							|  |  |  | 		return append(b, s...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// hash together meta and BMT root hash using the pools | 
					
						
							|  |  |  | 	return doSum(sw.pool.hasher(), b, meta, s) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | // writeSection writes the hash of i-th section into level 1 node of the BMT tree | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | func (h *Hasher) writeSection(i int, section []byte, double bool, final bool) { | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	// select the leaf node for the section | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 	var n *node | 
					
						
							|  |  |  | 	var isLeft bool | 
					
						
							|  |  |  | 	var hasher hash.Hash | 
					
						
							|  |  |  | 	var level int | 
					
						
							|  |  |  | 	t := h.getTree() | 
					
						
							|  |  |  | 	if double { | 
					
						
							|  |  |  | 		level++ | 
					
						
							|  |  |  | 		n = t.leaves[i] | 
					
						
							|  |  |  | 		hasher = n.hasher | 
					
						
							|  |  |  | 		isLeft = n.isLeft | 
					
						
							|  |  |  | 		n = n.parent | 
					
						
							|  |  |  | 		// hash the section | 
					
						
							|  |  |  | 		section = doSum(hasher, nil, section) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		n = t.leaves[i/2] | 
					
						
							|  |  |  | 		hasher = n.hasher | 
					
						
							|  |  |  | 		isLeft = i%2 == 0 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	// write hash into parent node | 
					
						
							|  |  |  | 	if final { | 
					
						
							|  |  |  | 		// for the last segment use writeFinalNode | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		h.writeFinalNode(level, n, hasher, isLeft, section) | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		h.writeNode(n, hasher, isLeft, section) | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | // writeNode pushes the data to the node | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // if it is the first of 2 sisters written, the routine terminates | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // if it is the second, it calculates the hash and writes it | 
					
						
							|  |  |  | // to the parent node recursively | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // since hashing the parent is synchronous the same hasher can be used | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | func (h *Hasher) writeNode(n *node, bh hash.Hash, isLeft bool, s []byte) { | 
					
						
							|  |  |  | 	level := 1 | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		// at the root of the bmt just write the result to the result channel | 
					
						
							|  |  |  | 		if n == nil { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 			h.getTree().result <- s | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		// otherwise assign child hash to left or right segment | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		if isLeft { | 
					
						
							|  |  |  | 			n.left = s | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			n.right = s | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		// the child-thread first arriving will terminate | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		if n.toggle() { | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		// the thread coming second now can be sure both left and right children are written | 
					
						
							|  |  |  | 		// so it calculates the hash of left|right and pushes it to the parent | 
					
						
							|  |  |  | 		s = doSum(bh, nil, n.left, n.right) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		isLeft = n.isLeft | 
					
						
							|  |  |  | 		n = n.parent | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		level++ | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | // writeFinalNode is following the path starting from the final datasegment to the | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | // BMT root via parents | 
					
						
							|  |  |  | // for unbalanced trees it fills in the missing right sister nodes using | 
					
						
							|  |  |  | // the pool's lookup table for BMT subtree root hashes for all-zero sections | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | // otherwise behaves like `writeNode` | 
					
						
							|  |  |  | func (h *Hasher) writeFinalNode(level int, n *node, bh hash.Hash, isLeft bool, s []byte) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		// at the root of the bmt just write the result to the result channel | 
					
						
							|  |  |  | 		if n == nil { | 
					
						
							|  |  |  | 			if s != nil { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 				h.getTree().result <- s | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		var noHash bool | 
					
						
							|  |  |  | 		if isLeft { | 
					
						
							|  |  |  | 			// coming from left sister branch | 
					
						
							|  |  |  | 			// when the final section's path is going via left child node | 
					
						
							|  |  |  | 			// we include an all-zero subtree hash for the right level and toggle the node. | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 			n.right = h.pool.zerohashes[level] | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 			if s != nil { | 
					
						
							|  |  |  | 				n.left = s | 
					
						
							|  |  |  | 				// if a left final node carries a hash, it must be the first (and only thread) | 
					
						
							|  |  |  | 				// so the toggle is already in passive state no need no call | 
					
						
							|  |  |  | 				// yet thread needs to carry on pushing hash to parent | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 				noHash = false | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				// if again first thread then propagate nil and calculate no hash | 
					
						
							|  |  |  | 				noHash = n.toggle() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// right sister branch | 
					
						
							|  |  |  | 			if s != nil { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 				// if hash was pushed from right child node, write right segment change state | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 				n.right = s | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 				// if toggle is true, we arrived first so no hashing just push nil to parent | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 				noHash = n.toggle() | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 				// if s is nil, then thread arrived first at previous node and here there will be two, | 
					
						
							|  |  |  | 				// so no need to do anything and keep s = nil for parent | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 				noHash = true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// the child-thread first arriving will just continue resetting s to nil | 
					
						
							|  |  |  | 		// the second thread now can be sure both left and right children are written | 
					
						
							|  |  |  | 		// it calculates the hash of left|right and pushes it to the parent | 
					
						
							|  |  |  | 		if noHash { | 
					
						
							|  |  |  | 			s = nil | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 			s = doSum(bh, nil, n.left, n.right) | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | 		// iterate to parent | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 		isLeft = n.isLeft | 
					
						
							|  |  |  | 		n = n.parent | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 		level++ | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // getTree obtains a BMT resource by reserving one from the pool and assigns it to the bmt field | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func (h *Hasher) getTree() *tree { | 
					
						
							|  |  |  | 	if h.bmt != nil { | 
					
						
							|  |  |  | 		return h.bmt | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	t := h.pool.reserve() | 
					
						
							|  |  |  | 	h.bmt = t | 
					
						
							|  |  |  | 	return t | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // atomic bool toggle implementing a concurrent reusable 2-state object | 
					
						
							|  |  |  | // atomic addint with %2 implements atomic bool toggle | 
					
						
							|  |  |  | // it returns true if the toggler just put it in the active/waiting state | 
					
						
							|  |  |  | func (n *node) toggle() bool { | 
					
						
							|  |  |  | 	return atomic.AddInt32(&n.state, 1)%2 == 1 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | // calculates the hash of the data using hash.Hash | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | func doSum(h hash.Hash, b []byte, data ...[]byte) []byte { | 
					
						
							| 
									
										
										
										
											2018-07-09 14:11:49 +02:00
										 |  |  | 	h.Reset() | 
					
						
							|  |  |  | 	for _, v := range data { | 
					
						
							|  |  |  | 		h.Write(v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return h.Sum(b) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-18 12:09:38 +02:00
										 |  |  | // hashstr is a pretty printer for bytes used in tree.draw | 
					
						
							| 
									
										
										
										
											2018-06-20 14:06:27 +02:00
										 |  |  | func hashstr(b []byte) string { | 
					
						
							|  |  |  | 	end := len(b) | 
					
						
							|  |  |  | 	if end > 4 { | 
					
						
							|  |  |  | 		end = 4 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fmt.Sprintf("%x", b[:end]) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // calculateDepthFor calculates the depth (number of levels) in the BMT tree | 
					
						
							|  |  |  | func calculateDepthFor(n int) (d int) { | 
					
						
							|  |  |  | 	c := 2 | 
					
						
							|  |  |  | 	for ; c < n; c *= 2 { | 
					
						
							|  |  |  | 		d++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return d + 1 | 
					
						
							|  |  |  | } |