Files
core/warehouse/Merkle.go
Akilan Selvacoumar 9775525d35 gofmt peernet (#103)
2023-02-12 09:59:31 +00:00

121 lines
3.7 KiB
Go

/*
File Name: Merkle.go
Copyright: 2021 Peernet Foundation s.r.o.
Author: Peter Kleissner
*/
package warehouse
import (
"errors"
"io"
"os"
"path/filepath"
"github.com/PeernetOfficial/core/merkle"
)
// Merkle companion files are created to store the entire merkle tree for files that are bigger than the minimum fragment size.
const merkleCompanionExt = ".merkle"
// MerkleFileExists checks if the merkle companion file exists. It returns StatusInvalidHash, StatusFileNotFound, or StatusOK.
func (wh *Warehouse) MerkleFileExists(hash []byte) (path string, fileSize uint64, status int, err error) {
hashA, err := ValidateHash(hash)
if err != nil {
return "", 0, StatusInvalidHash, err
}
a, b := buildPath(wh.Directory, hashA)
path = filepath.Join(a, b) + merkleCompanionExt
if fileInfo, err := os.Stat(path); err == nil {
// file exists
return path, uint64(fileInfo.Size()), StatusOK, nil
}
return "", 0, StatusFileNotFound, os.ErrNotExist
}
// createMerkleCompanionFile creates a merkle companion file. If the merkle companion file already exists, it is overwritten.
// dataFilePath is the full path to the data file in the warehouse.
func (wh *Warehouse) createMerkleCompanionFile(dataFilePath string) (status int, err error) {
// open the data file
dataFile, err := os.Open(dataFilePath)
if err != nil && os.IsNotExist(err) {
return StatusFileNotFound, err
} else if err != nil {
return StatusErrorOpenFile, err
}
defer dataFile.Close()
var fileSize uint64
stat, err := dataFile.Stat()
if err != nil {
return StatusErrorOpenFile, err
}
fileSize = uint64(stat.Size())
// Merkle files are only created if merkle trees are actually used, which means the file must be bigger than the minimum fragment size.
// Otherwise the merkle root hash will be just the file hash and the merkle overhead provides no advantage whatsoever.
if fileSize <= merkle.MinimumFragmentSize {
return StatusOK, nil
}
// Create a new merkle file. If one exists, overwrite.
merkleFile := dataFilePath + merkleCompanionExt
fileM, err := os.OpenFile(merkleFile, os.O_WRONLY|os.O_CREATE, 0666) // 666 = All uses can read/write
if err != nil {
return StatusErrorCreateTarget, err
}
defer fileM.Close()
// create the merkle tree and write it to the companion file
fragmentSize := merkle.CalculateFragmentSize(fileSize)
tree, err := merkle.NewMerkleTree(fileSize, fragmentSize, dataFile)
if err != nil {
return StatusErrorCreateMerkle, err
}
fileM.Write(tree.Export())
return StatusOK, nil
}
// ReadMerkleTree reads the merkle tree from the companion file associated with the hash.
// It is the callers responsibility to first check if a merkle tree file is to be expected (files smaller or equal than the minimum fragment size do not use a merkle tree).
func (wh *Warehouse) ReadMerkleTree(hash []byte, headerOnly bool) (tree *merkle.MerkleTree, status int, err error) {
path, _, status, err := wh.MerkleFileExists(hash)
if status != StatusOK { // file does not exist or invalid hash
return nil, status, err
}
fileM, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return nil, StatusErrorOpenFile, err
}
defer fileM.Close()
if headerOnly {
data := make([]byte, merkle.MerkleTreeFileHeaderSize)
if _, err = io.ReadFull(fileM, data); err != nil {
return nil, StatusErrorReadFile, err
}
if tree = merkle.ReadMerkleTreeHeader(data); tree == nil {
return nil, StatusErrorMerkleTreeFile, errors.New("invalid merkle tree file header")
}
} else {
data, err := io.ReadAll(fileM)
if err != nil {
return nil, StatusErrorReadFile, err
}
if tree = merkle.ImportMerkleTree(data); tree == nil {
return nil, StatusErrorMerkleTreeFile, errors.New("invalid merkle tree file header")
}
}
return tree, StatusOK, nil
}