/* File Name: Merkle Tree.go Copyright: 2021 Peernet s.r.o. Author: Peter Kleissner Generates the merkle tree based on input data. In case of uneven number of fragments, the last uneven fragment is moved up a level. */ package merkle import ( "bytes" "encoding/binary" "errors" "io" "lukechampine.com/blake3" ) // MerkleTree represents an entire merkle tree type MerkleTree struct { // information about the original file FileSize uint64 FragmentSize uint64 FragmentCount uint64 // list of hashes FragmentHashes [][]byte // List of hashes for each fragment MiddleHashes [][][]byte // All hashes in the middle, bottom up. RootHash []byte // Root hash. } // NewMerkleTree creates a new merkle tree from the input func NewMerkleTree(fileSize, fragmentSize uint64, reader io.Reader) (tree *MerkleTree, err error) { if fragmentSize == 0 { return nil, errors.New("invalid fragment size") } tree = &MerkleTree{ FileSize: fileSize, FragmentSize: fragmentSize, FragmentCount: fileSizeToFragmentCount(fileSize, fragmentSize), } // Special case: No fragments, in case of empty data. if tree.FragmentCount == 0 { hash := blake3.Sum256(nil) tree.RootHash = hash[:] return tree, nil } else if tree.FragmentCount == 1 { // Special case: Single fragment. data := make([]byte, fileSize) if _, err := io.ReadAtLeast(reader, data, int(fileSize)); err != nil { return nil, err } hash := blake3.Sum256(data) tree.RootHash = hash[:] return tree, nil } // calculate the hash per fragment data := make([]byte, fragmentSize) remaining := fileSize for n := uint64(0); n < tree.FragmentCount; n++ { if fragmentSize > remaining { fragmentSize = remaining } if _, err := io.ReadAtLeast(reader, data, int(fragmentSize)); err != nil { return nil, err } // hash the fragment hash := blake3.Sum256(data[:fragmentSize]) tree.FragmentHashes = append(tree.FragmentHashes, hash[:]) remaining -= fragmentSize } // calculate the intermediate hashes tree.calculateMiddleHashes(0) return tree, nil } func fileSizeToFragmentCount(fileSize, fragmentSize uint64) (count uint64) { return (fileSize + fragmentSize - 1) / fragmentSize } func (tree *MerkleTree) calculateMiddleHashes(level uint64) { if len(tree.FragmentHashes) == 0 { return } var newHashes, inputHashes [][]byte if level == 0 { inputHashes = tree.FragmentHashes } else { inputHashes = tree.MiddleHashes[level-1] } for n := 0; n+1 <= len(inputHashes)-1; n += 2 { newHashes = append(newHashes, calculateMiddleHash(inputHashes[n], inputHashes[n+1])) } // Uneven leafs? in this case the new hash is just a copy of the uneven one. No point in artifically recalcualting it with itself like Bitcoin does. // For other possible implementations see https://medium.com/coinmonks/merkle-trees-concepts-and-use-cases-5da873702318. if len(inputHashes)%2 != 0 { newHashes = append(newHashes, inputHashes[len(inputHashes)-1]) } if len(newHashes) == 1 { // Only one hash generated. tree.RootHash = newHashes[0] } else if len(newHashes) > 1 { tree.MiddleHashes = append(tree.MiddleHashes, newHashes) tree.calculateMiddleHashes(level + 1) } } func calculateMiddleHash(hash1 []byte, hash2 []byte) (newHash []byte) { var data []byte data = append(data, hash1...) data = append(data, hash2...) hash := blake3.Sum256(data) return hash[:] } // CreateVerification returns the verification hashes for the given fragment number. The root hash itself is not included. // The result might be empty if there is no or a single fragment. // Each verification hash has a preceding left (= 0)/right (= 1) indicator that indicates where the verification is positioned. // This makes the algorithm future proof, in case uneven leafs will be handled differently. func (tree *MerkleTree) CreateVerification(fragment uint64) (verificationHashes [][]byte) { // 0 fragments: Empty data. // 1 fragment: The hash of the fragment is the root hash. if tree.FragmentCount <= 1 { return nil } else if fragment >= tree.FragmentCount { // invalid fragment index return nil } // first hash it he neighbor fragment hash, if available if fragment == tree.FragmentCount-1 && fragment%2 == 0 { } else if fragment%2 == 0 { verificationHashes = append(verificationHashes, append([]byte{1}, tree.FragmentHashes[fragment+1]...)) } else { verificationHashes = append(verificationHashes, append([]byte{0}, tree.FragmentHashes[fragment-1]...)) } // go through all middle hash levels for n := 0; n < len(tree.MiddleHashes); n++ { fragment = fragment / 2 if fragment == uint64(len(tree.MiddleHashes[n])-1) && fragment%2 == 0 { } else if fragment%2 == 0 { verificationHashes = append(verificationHashes, append([]byte{1}, tree.MiddleHashes[n][fragment+1]...)) } else { verificationHashes = append(verificationHashes, append([]byte{0}, tree.MiddleHashes[n][fragment-1]...)) } } return } // MerkleVerify validates the hashed data against the verification hashes and the known root hash. func MerkleVerify(rootHash []byte, dataHash []byte, verificationHashes [][]byte) (valid bool) { for _, verifyHash := range verificationHashes { if verifyHash[0] == 0 { dataHash = calculateMiddleHash(verifyHash[1:], dataHash) } else { dataHash = calculateMiddleHash(dataHash, verifyHash[1:]) } } return bytes.Equal(rootHash, dataHash) } /* 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 func (tree *MerkleTree) Export() (data []byte) { 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 func ImportMerkleTree(data []byte) (tree *MerkleTree) { if tree = ReadMerkleTreeHeader(data); tree == nil || 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 } // ReadMerkleTreeHeader reads the merkle tree header. Fragment and middle hashes are not loaded. func ReadMerkleTreeHeader(data []byte) (tree *MerkleTree) { // Read the header. Enforce the minimum size. if len(data) < MerkleTreeFileHeaderSize { 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] return tree }