mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
Merkle tree: export/import functions. #48
This commit is contained in:
@@ -4,13 +4,14 @@ Copyright: 2021 Peernet s.r.o.
|
|||||||
Author: Peter Kleissner
|
Author: Peter Kleissner
|
||||||
|
|
||||||
Generates the merkle tree based on input data.
|
Generates the merkle tree based on input data.
|
||||||
In case of uneven number of fragments, the last fragment will be hashed against the top hash of all the left tree to create the merkle root hash.
|
In case of uneven number of fragments, the last uneven fragment is moved up a level.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package fragment
|
package fragment
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
@@ -26,8 +27,8 @@ type MerkleTree struct {
|
|||||||
|
|
||||||
// list of hashes
|
// list of hashes
|
||||||
fragmentHashes [][]byte // List of hashes for each fragment
|
fragmentHashes [][]byte // List of hashes for each fragment
|
||||||
rootHash []byte // Root hash.
|
|
||||||
middleHashes [][][]byte // All hashes in the middle, bottom up.
|
middleHashes [][][]byte // All hashes in the middle, bottom up.
|
||||||
|
rootHash []byte // Root hash.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMerkleTree creates a new merkle tree from the input
|
// NewMerkleTree creates a new merkle tree from the input
|
||||||
@@ -185,15 +186,121 @@ func MerkleVerify(rootHash []byte, dataHash []byte, verificationHashes [][]byte)
|
|||||||
return bytes.Equal(rootHash, dataHash)
|
return bytes.Equal(rootHash, dataHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export/Import of the merkle tree structure:
|
/*
|
||||||
// TODO
|
Export/Import of the merkle tree structure:
|
||||||
|
|
||||||
|
Offset Size Info
|
||||||
|
0 8 File Size
|
||||||
|
8 8 Fragment Size
|
||||||
|
16 32 Merkle Root Hash
|
||||||
|
48 32 * n Fragment Hashes
|
||||||
|
? 32 * n Middle Hashes
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
const merkleTreeFileHeaderSize = 8 + 8 + 32
|
||||||
|
|
||||||
|
// calculateTotalHashCount returns the total number of fragment and middle hashes needed for the given count of fragments
|
||||||
|
func calculateTotalHashCount(fragmentCount uint64) (count uint64) {
|
||||||
|
// Special case no or 1 fragment: None needed, since the fragment hash is directly stored as root hash.
|
||||||
|
if fragmentCount <= 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal count of fragment hashes needed
|
||||||
|
count = fragmentCount
|
||||||
|
|
||||||
|
// Calculate middle hashes number
|
||||||
|
for countHashesLast := fragmentCount; ; {
|
||||||
|
countMiddleNew := (countHashesLast + 1) / 2 // round up
|
||||||
|
if countMiddleNew <= 1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
count += countMiddleNew
|
||||||
|
countHashesLast = countMiddleNew
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
// Export stores the tree as blob
|
// Export stores the tree as blob
|
||||||
func (tree *MerkleTree) Export() (data []byte) {
|
func (tree *MerkleTree) Export() (data []byte) {
|
||||||
return nil
|
data = make([]byte, merkleTreeFileHeaderSize+calculateTotalHashCount(tree.fragmentCount)*32)
|
||||||
|
|
||||||
|
// header
|
||||||
|
binary.LittleEndian.PutUint64(data[0:8], tree.fileSize)
|
||||||
|
binary.LittleEndian.PutUint64(data[8:16], tree.fragmentSize)
|
||||||
|
copy(data[16:16+32], tree.rootHash)
|
||||||
|
|
||||||
|
// fragment hashes
|
||||||
|
offset := 48
|
||||||
|
for _, hash := range tree.fragmentHashes {
|
||||||
|
copy(data[offset:offset+32], hash)
|
||||||
|
offset += 32
|
||||||
|
}
|
||||||
|
|
||||||
|
// middle hashes
|
||||||
|
for n := 0; n < len(tree.middleHashes); n++ {
|
||||||
|
for _, hash := range tree.middleHashes[n] {
|
||||||
|
copy(data[offset:offset+32], hash)
|
||||||
|
offset += 32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data[:offset]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import reads the tree from the input data
|
// Import reads the tree from the input data
|
||||||
func (tree *MerkleTree) Import(data []byte) {
|
func ImportMerkleTree(data []byte) (tree *MerkleTree) {
|
||||||
|
// Read the header. Enforce the minimum size.
|
||||||
|
if len(data) < 8+8+32 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tree = &MerkleTree{
|
||||||
|
fileSize: binary.LittleEndian.Uint64(data[0:8]),
|
||||||
|
fragmentSize: binary.LittleEndian.Uint64(data[8:16]),
|
||||||
|
}
|
||||||
|
tree.fragmentCount = fileSizeToFragmentCount(tree.fileSize, tree.fragmentSize)
|
||||||
|
tree.rootHash = data[16 : 16+32]
|
||||||
|
|
||||||
|
if tree.fragmentCount <= 1 {
|
||||||
|
return tree
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify size
|
||||||
|
if uint64(len(data)) < merkleTreeFileHeaderSize+calculateTotalHashCount(tree.fragmentCount)*32 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fragment hashes
|
||||||
|
offset := 48
|
||||||
|
for n := 0; n < int(tree.fragmentCount); n++ {
|
||||||
|
hash := data[offset : offset+32]
|
||||||
|
tree.fragmentHashes = append(tree.fragmentHashes, hash)
|
||||||
|
offset += 32
|
||||||
|
}
|
||||||
|
|
||||||
|
// middle hashes
|
||||||
|
n := tree.fragmentCount / 2
|
||||||
|
if tree.fragmentCount > 2 && tree.fragmentCount%2 != 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; n > 1; n = n / 2 {
|
||||||
|
var hashList [][]byte
|
||||||
|
for m := uint64(0); m < n; m++ {
|
||||||
|
hash := data[offset : offset+32]
|
||||||
|
hashList = append(hashList, hash)
|
||||||
|
offset += 32
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.middleHashes = append(tree.middleHashes, hashList)
|
||||||
|
if len(hashList)%2 != 0 {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,3 +68,72 @@ func printMerkleTree(tree *MerkleTree) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMerkleFileExport(t *testing.T) {
|
||||||
|
dataSize := uint64(11*1024*1024 + 100)
|
||||||
|
data := make([]byte, dataSize)
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(rand.Reader, data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fragmentSize := CalculateFragmentSize(dataSize)
|
||||||
|
|
||||||
|
tree, err := NewMerkleTree(dataSize, fragmentSize, bytes.NewBuffer(data))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error creating merkle tree: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
printMerkleTree(tree)
|
||||||
|
|
||||||
|
treeData := tree.Export()
|
||||||
|
|
||||||
|
tree2 := ImportMerkleTree(treeData)
|
||||||
|
if tree2 == nil {
|
||||||
|
fmt.Printf("Error importing tree\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
printMerkleTree(tree2)
|
||||||
|
|
||||||
|
// verify both trees
|
||||||
|
if tree.fileSize != tree2.fileSize || tree.fragmentSize != tree2.fragmentSize || tree.fragmentCount != tree2.fragmentCount {
|
||||||
|
fmt.Printf("Error: Header of trees mismatch\n")
|
||||||
|
return
|
||||||
|
} else if !bytes.Equal(tree.rootHash, tree2.rootHash) {
|
||||||
|
fmt.Printf("Error: Merkle root hash mismatch\n")
|
||||||
|
return
|
||||||
|
} else if len(tree.fragmentHashes) != len(tree2.fragmentHashes) {
|
||||||
|
fmt.Printf("Error: Fragment hashes mismatch count\n")
|
||||||
|
return
|
||||||
|
} else if len(tree.middleHashes) != len(tree2.middleHashes) {
|
||||||
|
fmt.Printf("Error: Middle hashes level mismatch\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// fragment hashes and middle hashes
|
||||||
|
for n, hash := range tree.fragmentHashes {
|
||||||
|
if !bytes.Equal(hash, tree2.fragmentHashes[n]) {
|
||||||
|
fmt.Printf("Error: Fragment hash %d mismatch\n", n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for n := range tree.middleHashes {
|
||||||
|
if len(tree.middleHashes[n]) != len(tree2.middleHashes[n]) {
|
||||||
|
fmt.Printf("Error: Middle hashes level %d mismatch\n", n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for m, hash := range tree.middleHashes[n] {
|
||||||
|
if !bytes.Equal(hash, tree2.middleHashes[n][m]) {
|
||||||
|
fmt.Printf("Error: Middle hash %d %d mismatch\n", n, m)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Success. Import/export match.\n")
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user