Skip to content

Commit b3e4eee

Browse files
mpolitzervfusco
authored andcommitted
feat(merkle): Add merkle with repetitions
1 parent 3f349bb commit b3e4eee

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

internal/merkle/repetitions.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// (c) Cartesi and individual authors (see AUTHORS)
2+
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
3+
4+
package merkle
5+
6+
import (
7+
"cmp"
8+
"fmt"
9+
"slices"
10+
11+
"github.com/ethereum/go-ethereum/common"
12+
"github.com/ethereum/go-ethereum/crypto"
13+
)
14+
15+
type Node struct {
16+
hash common.Hash
17+
until uint64 // (count of) elements to the left
18+
}
19+
20+
func nodeCmp(x Node, until uint64) int {
21+
return cmp.Compare(x.until, until)
22+
}
23+
24+
func Append(xs []Node, hash common.Hash, reps uint64) []Node {
25+
if len(xs) == 0 {
26+
return []Node{{hash, reps}}
27+
}
28+
29+
latest := &xs[len(xs)-1]
30+
if latest.hash == hash { // reuse previous Node when repeating the hash
31+
xs[len(xs)-1].until += reps
32+
return xs
33+
}
34+
return append(xs, Node{hash, latest.until + reps})
35+
}
36+
37+
// Merkle with repetitions receives an ORDERED array of nodes, log2size and a
38+
// stride to compute the root hash of subtree in the range `a` - `b`.
39+
//
40+
// . |
41+
// / \ |
42+
// / \ |
43+
// / \ |
44+
// / \ |
45+
// / /\ \ |
46+
// +--/--\-----+ v log2size
47+
// 0 1 2 3 4 <- stride
48+
// a--b
49+
func GetRootHash(nodes []Node, log2size uint64, stride uint64) (common.Hash, error) {
50+
zero := common.Hash{}
51+
52+
aIndex := (1 << log2size) * stride
53+
bIndex := (1 << log2size) * (stride + 1)
54+
55+
limit := nodes[len(nodes)-1].until
56+
if limit < bIndex {
57+
return zero, fmt.Errorf("Index out of bounds: %v out of %v.", bIndex, limit)
58+
}
59+
60+
aCell, _ := slices.BinarySearchFunc(nodes, aIndex, nodeCmp)
61+
bCell, _ := slices.BinarySearchFunc(nodes, bIndex, nodeCmp)
62+
if aCell == bCell {
63+
return repeatedMerkleRoot(nodes[aCell].hash, log2size)
64+
}
65+
66+
lhs, _ := GetRootHash(nodes[aCell:bCell], log2size-1, 2*stride)
67+
rhs, _ := GetRootHash(nodes[aCell:bCell], log2size-1, 2*stride+1)
68+
return crypto.Keccak256Hash(lhs[:], rhs[:]), nil
69+
}
70+
71+
// level is a power of 2
72+
func repeatedMerkleRoot(hash common.Hash, log2level uint64) (common.Hash, error) {
73+
for range log2level {
74+
hash = crypto.Keccak256Hash(hash[:], hash[:])
75+
}
76+
return hash, nil
77+
}

0 commit comments

Comments
 (0)