Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hasher): re-use merkleize code #16

Closed
wants to merge 6 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 45 additions & 23 deletions hasher.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,12 +422,7 @@ func (h *Hasher) insertChunk(chunk [32]byte, depth int) {
// We've reached **exactly** the batch number of chunks. Note, we're adding
// them one by one, so can't all of a sudden overshoot. Hash the next batch
// of chunks and update the trackers.
chunks := len(h.chunks)
gohashtree.HashChunks(h.chunks[chunks-hasherBatch:], h.chunks[chunks-hasherBatch:])
h.chunks = h.chunks[:chunks-hasherBatch/2]

group.depth++
group.chunks >>= 1
h.merkleizeAndCollapseChunks(group, len(h.chunks)-hasherBatch)

// The last group tracker we've just hashed needs to be either updated to
// the new level count, or merged into the previous one if they share all
Expand Down Expand Up @@ -489,23 +484,17 @@ func (h *Hasher) ascendLayer(capacity uint64) {

// Last group was reduced to a single root hash. If the capacity used during
// hashing it was less than what the container slot required, keep expanding
// it with empty sibling tries.
// it with empty sibling tries. The effective purpose of this loop is to expand
// the last group with virtual zero chunks until it reaches the required capacity
for {
groups := len(h.groups)

// If we've used up the required capacity, stop expanding
group := h.groups[groups-1]
group := h.groups[len(h.groups)-1]
if (1 << group.depth) >= capacity {
break
}
// Last group requires expansion, hash in a new empty sibling trie
h.chunks = append(h.chunks, hasherZeroCache[group.depth])

chunks := len(h.chunks)
gohashtree.HashChunks(h.chunks[chunks-2:], h.chunks[chunks-2:])
h.chunks = h.chunks[:chunks-1]

h.groups[groups-1].depth++
// Expand the group by merkleizing it with virtual zero chunks
h.merkelizeWithVirtualZeroes(group)
}
// Ascend from the previous hashing layer
h.layer--
Expand Down Expand Up @@ -537,6 +526,7 @@ func (h *Hasher) balanceLayer() {
return
}
}

// Either group has multiple chunks still, or there are multiple entire
// groups in this layer. Either way, we need to collapse this group into
// the previous one and then see.
Expand All @@ -545,12 +535,7 @@ func (h *Hasher) balanceLayer() {
h.chunks = append(h.chunks, hasherZeroCache[group.depth])
group.chunks++
}
chunks := len(h.chunks)
gohashtree.HashChunks(h.chunks[chunks-int(group.chunks):], h.chunks[chunks-int(group.chunks):])
h.chunks = h.chunks[:chunks-int(group.chunks)>>1]

group.depth++
group.chunks >>= 1
h.merkleizeAndCollapseChunks(group, len(h.chunks)-int(group.chunks))

// The last group tracker we've just hashed needs to be either updated to
// the new level count, or merged into the previous one if they share all
Expand All @@ -570,6 +555,43 @@ func (h *Hasher) balanceLayer() {
}
}

// merkleizeAndCollapseChunks hashes the chunks in the group and collapses them.
//
// CONTRACT: len(h.chunks) must be EVEN.
func (h *Hasher) merkleizeAndCollapseChunks(group groupStats, startIndex int) {
// Hash the chunks.
gohashtree.HashChunks(h.chunks[startIndex:], h.chunks[startIndex:])

// Cut the size of the chunks slice in half, as we've just halved the number
// of chunks in it by hasing.
h.chunks = h.chunks[:startIndex>>1]

// Increase the group depth since we have gone up the tree.
group.depth++

// Divide the number of chunks in the next group by two since we have just
// hashed them.
group.chunks >>= 1
}

// merkelizeWithVirtualZeroes hashes the chunks in the group and collapses them.
// This function is part of a larger Merkle tree hashing algorithm.
func (h *Hasher) merkelizeWithVirtualZeroes(group groupStats) {
// Append a zero hash to the chunks slice.
// This adds a virtual zero node to balance the tree.
h.chunks = append(h.chunks, hasherZeroCache[group.depth])

// Hash the last two chunks (the last real chunk and the virtual zero).
chunks := len(h.chunks)
gohashtree.HashChunks(h.chunks[chunks-2:], h.chunks[chunks-2:])

// Remove the virtual zero chunk after hashing.
h.chunks = h.chunks[:chunks-1]

// Increment the depth of the last group, moving up one level in the tree.
h.groups[len(h.groups)-1].depth++
}

// ascendMixinLayer is similar to ascendLayer, but actually ascends one for the
// data content, and then mixes in the provided length and ascends once more.
func (h *Hasher) ascendMixinLayer(size uint64, chunks uint64) {
Expand Down
Loading