aboutsummaryrefslogtreecommitdiff
path: root/internal/merkle/tree.go
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-26 15:41:45 -0800
committerFuwn <[email protected]>2026-02-26 15:41:45 -0800
commitfec9114caaa7d274e524793d5eb0cf2ef2c5af11 (patch)
tree0897394ccdfaf6633e1a4ca8eb02bff49bb93c00 /internal/merkle/tree.go
parentfeat: add read-only PLC API compatibility endpoints (diff)
downloadplutia-test-fec9114caaa7d274e524793d5eb0cf2ef2c5af11.tar.xz
plutia-test-fec9114caaa7d274e524793d5eb0cf2ef2c5af11.zip
feat: Apply Iku formatting
Diffstat (limited to 'internal/merkle/tree.go')
-rw-r--r--internal/merkle/tree.go49
1 files changed, 49 insertions, 0 deletions
diff --git a/internal/merkle/tree.go b/internal/merkle/tree.go
index ff97217..023b4ec 100644
--- a/internal/merkle/tree.go
+++ b/internal/merkle/tree.go
@@ -12,27 +12,36 @@ type Sibling struct {
func HashLeaf(leaf []byte) []byte {
s := sha256.Sum256(leaf)
+
return s[:]
}
func Root(leaves [][]byte) []byte {
if len(leaves) == 0 {
empty := sha256.Sum256(nil)
+
return empty[:]
}
+
level := cloneLevel(leaves)
+
for len(level) > 1 {
next := make([][]byte, 0, (len(level)+1)/2)
+
for i := 0; i < len(level); i += 2 {
left := level[i]
right := left
+
if i+1 < len(level) {
right = level[i+1]
}
+
next = append(next, hashPair(left, right))
}
+
level = next
}
+
return level[0]
}
@@ -40,15 +49,19 @@ func BuildProof(leaves [][]byte, index int) []Sibling {
if len(leaves) == 0 || index < 0 || index >= len(leaves) {
return nil
}
+
proof := make([]Sibling, 0)
level := cloneLevel(leaves)
idx := index
+
for len(level) > 1 {
if idx%2 == 0 {
sib := idx + 1
+
if sib >= len(level) {
sib = idx
}
+
proof = append(proof, Sibling{Hash: hex.EncodeToString(level[sib]), Left: false})
} else {
sib := idx - 1
@@ -56,44 +69,57 @@ func BuildProof(leaves [][]byte, index int) []Sibling {
}
next := make([][]byte, 0, (len(level)+1)/2)
+
for i := 0; i < len(level); i += 2 {
left := level[i]
right := left
+
if i+1 < len(level) {
right = level[i+1]
}
+
next = append(next, hashPair(left, right))
}
+
idx /= 2
level = next
}
+
return proof
}
func VerifyProof(leafHash []byte, proof []Sibling, root []byte) bool {
cur := append([]byte(nil), leafHash...)
+
for _, s := range proof {
sib, err := hex.DecodeString(s.Hash)
+
if err != nil {
return false
}
+
var combined []byte
+
if s.Left {
combined = append(append([]byte(nil), sib...), cur...)
} else {
combined = append(append([]byte(nil), cur...), sib...)
}
+
h := sha256.Sum256(combined)
cur = h[:]
}
+
return hex.EncodeToString(cur) == hex.EncodeToString(root)
}
func cloneLevel(in [][]byte) [][]byte {
out := make([][]byte, len(in))
+
for i := range in {
out[i] = append([]byte(nil), in[i]...)
}
+
return out
}
@@ -110,19 +136,26 @@ func (a *Accumulator) AddLeafHash(leafHash []byte) {
if len(leafHash) == 0 {
return
}
+
cur := append([]byte(nil), leafHash...)
level := 0
+
for {
if level >= len(a.levels) {
a.levels = append(a.levels, nil)
}
+
if a.levels[level] == nil {
a.levels[level] = cur
+
a.pendingCount++
+
break
}
+
cur = hashPair(a.levels[level], cur)
a.levels[level] = nil
+
a.pendingCount--
level++
}
@@ -131,40 +164,53 @@ func (a *Accumulator) AddLeafHash(leafHash []byte) {
func (a *Accumulator) RootDuplicateLast() []byte {
if a.pendingCount == 0 {
empty := sha256.Sum256(nil)
+
return empty[:]
}
+
clone := &Accumulator{
levels: cloneLevel(a.levels),
pendingCount: a.pendingCount,
}
+
for clone.pendingCount > 1 {
level := clone.lowestPendingLevel()
cur := clone.levels[level]
clone.levels[level] = nil
+
clone.pendingCount--
cur = hashPair(cur, cur)
+
level++
+
for {
if level >= len(clone.levels) {
clone.levels = append(clone.levels, nil)
}
+
if clone.levels[level] == nil {
clone.levels[level] = cur
+
clone.pendingCount++
+
break
}
+
cur = hashPair(clone.levels[level], cur)
clone.levels[level] = nil
+
clone.pendingCount--
level++
}
}
+
return clone.highestPendingHash()
}
func hashPair(left, right []byte) []byte {
h := sha256.Sum256(append(append([]byte(nil), left...), right...))
+
return h[:]
}
@@ -174,6 +220,7 @@ func (a *Accumulator) lowestPendingLevel() int {
return i
}
}
+
return 0
}
@@ -183,6 +230,8 @@ func (a *Accumulator) highestPendingHash() []byte {
return a.levels[i]
}
}
+
empty := sha256.Sum256(nil)
+
return empty[:]
}