aboutsummaryrefslogtreecommitdiffstats
path: root/merkle.go
diff options
context:
space:
mode:
Diffstat (limited to 'merkle.go')
-rw-r--r--merkle.go75
1 files changed, 71 insertions, 4 deletions
diff --git a/merkle.go b/merkle.go
index 99fae7d..ec936c6 100644
--- a/merkle.go
+++ b/merkle.go
@@ -1,8 +1,10 @@
package main
import (
+ "bytes"
"encoding/binary"
"os"
+ "sync"
"golang.org/x/crypto/sha3"
)
@@ -10,7 +12,7 @@ import (
var hashSize = int64(sha3.New256().Size())
type Merkle interface {
- Build() []byte
+ Build(parallel bool) []byte
Put(id int64, data []byte)
Read(buf []byte, id int64)
Size() int64
@@ -56,8 +58,16 @@ func (m *BFSMerkle) Read(buf []byte, id int64) {
m.ReadAt(buf, id*hashSize)
}
+func (m *BFSMerkle) Build(parallel bool) []byte {
+ if parallel {
+ return m.parallelbuild()
+ } else {
+ return m.build()
+ }
+}
+
// disk access is sequential and mostly backward
-func (m *BFSMerkle) Build() []byte {
+func (m *BFSMerkle) build() []byte {
size := m.Size()
h := sha3.New256()
hsize := int64(h.Size())
@@ -82,6 +92,53 @@ func (m *BFSMerkle) Build() []byte {
return buf
}
+func (m *BFSMerkle) parallelbuild() []byte {
+ size := m.Size()
+ hsize := hashSize
+ var left int64
+ var wg sync.WaitGroup
+ p := 100
+
+ i := 0
+ for id := size / 2; id <= size; id++ {
+ wg.Add(1)
+ go func(id int64) {
+ defer wg.Done()
+ var buf bytes.Buffer
+ binary.Write(&buf, binary.LittleEndian, id)
+ hash := sha3.Sum256(buf.Bytes())
+ m.WriteAt(hash[:], id*hsize)
+ }(id)
+ if i%p == 0 {
+ wg.Wait()
+ }
+ i++
+ }
+
+ for height := m.height - 1; height >= 1; height-- {
+ left = 1 << uint64(height-1)
+ i := 0
+ for id := left - 1; id < 2*left-1; id++ {
+ wg.Add(1)
+ go func(id int64) {
+ defer wg.Done()
+ buf := make([]byte, 2*hsize)
+ m.ReadAt(buf, (id*2+1)*hsize)
+ m.ReadAt(buf[hsize:], (id*2+2)*hsize)
+ hash := sha3.Sum256(buf)
+ m.WriteAt(hash[:], id*hsize)
+ }(id)
+ if i%p == 0 {
+ wg.Wait()
+ }
+ i++
+ }
+ }
+ buf := make([]byte, hsize)
+ m.ReadAt(buf, 0)
+ return buf
+}
+
func (m *BFSMerkle) Proof(id int64) [][]byte {
proof := make([][]byte, m.height)
proof[0] = make([]byte, hashSize)
@@ -124,7 +181,9 @@ func (m *BFSMerkle) BatchProofs(ids []int64) [][][]byte {
for i := range proofs {
proofs[i][height] = make([]byte, hashSize)
id = tids[i]
- if id&1 == 0 {
+ if i > 0 && id == tids[i-1] {
+ proofs[i][height] = proofs[i-1][height]
+ } else if id&1 == 0 {
m.ReadAt(proofs[i][height], (id-1)*hashSize)
} else {
m.ReadAt(proofs[i][height], (id+1)*hashSize)
@@ -167,7 +226,7 @@ func (m *PostMerkle) Read(buf []byte, id int64) {
// Iterative post-order depth-first construction of the Merkle tree
// disk access is optimal and forward
-func (m *PostMerkle) Build() []byte {
+func (m *PostMerkle) Build(parallel bool) []byte {
size := m.Size()
h := sha3.New256()
hsize := int64(h.Size())
@@ -235,3 +294,11 @@ func (m *PostMerkle) Proof(id int64) [][]byte {
m.ReadAt(proof[0], cur*hashSize)
return proof
}
+
+func (m *PostMerkle) Proofs(ids []int64) [][][]byte {
+ proofs := make([][][]byte, len(ids))
+ for i, id := range ids {
+ proofs[i] = m.Proof(id)
+ }
+ return proofs
+}