From 793e586234f92f7ef31c1839e49a14f0946c8a9d Mon Sep 17 00:00:00 2001 From: Kleissner Date: Sun, 28 Nov 2021 22:03:08 +0100 Subject: [PATCH] webapi: Set merkle fields of file records when adding files to the blockchain. --- webapi/File.go | 59 ++++++++++++++++++++++++++++++++++++++++++--- webapi/Warehouse.go | 2 +- webapi/readme.md | 8 ++++-- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/webapi/File.go b/webapi/File.go index 293dede..0c9af21 100644 --- a/webapi/File.go +++ b/webapi/File.go @@ -12,6 +12,8 @@ import ( "github.com/PeernetOfficial/core" "github.com/PeernetOfficial/core/blockchain" + "github.com/PeernetOfficial/core/merkle" + "github.com/PeernetOfficial/core/protocol" "github.com/PeernetOfficial/core/warehouse" "github.com/google/uuid" ) @@ -38,7 +40,7 @@ type apiFile struct { Name string `json:"name"` // Name of the file Description string `json:"description"` // Description. This is expected to be multiline and contain hashtags! Date time.Time `json:"date"` // Date shared - NodeID []byte `json:"nodeid"` // Node ID, owner of the file + NodeID []byte `json:"nodeid"` // Node ID, owner of the file. Read only. Metadata []apiFileMetadata `json:"metadata"` // Additional metadata. } @@ -139,6 +141,10 @@ func apiBlockchainFileAdd(w http.ResponseWriter, r *http.Request) { var filesAdd []blockchain.BlockRecordFile for _, file := range input.Files { + if len(file.Hash) != protocol.HashSize { + http.Error(w, "", http.StatusBadRequest) + return + } if file.ID == uuid.Nil { // if the ID is not provided by the caller, set it file.ID = uuid.New() } @@ -154,9 +160,20 @@ func apiBlockchainFileAdd(w http.ResponseWriter, r *http.Request) { } else { file.Size = uint64(fileInfo.Size()) } + } else { + file.Hash = protocol.HashData(nil) + file.Size = 0 } - filesAdd = append(filesAdd, blockRecordFileFromAPI(file)) + blockRecord := blockRecordFileFromAPI(file) + + // Set the merkle tree info as appropriate. + if !setFileMerkleInfo(&blockRecord) { + EncodeJSON(w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse}) + return + } + + filesAdd = append(filesAdd, blockRecord) } newHeight, newVersion, status := core.UserBlockchain.AddFiles(filesAdd) @@ -233,7 +250,10 @@ func apiBlockchainFileUpdate(w http.ResponseWriter, r *http.Request) { var filesAdd []blockchain.BlockRecordFile for _, file := range input.Files { - if file.ID == uuid.Nil { // if the ID is not provided by the caller, abort + if len(file.Hash) != protocol.HashSize { + http.Error(w, "", http.StatusBadRequest) + return + } else if file.ID == uuid.Nil { // if the ID is not provided by the caller, abort http.Error(w, "", http.StatusBadRequest) return } @@ -249,9 +269,20 @@ func apiBlockchainFileUpdate(w http.ResponseWriter, r *http.Request) { } else { file.Size = uint64(fileInfo.Size()) } + } else { + file.Hash = protocol.HashData(nil) + file.Size = 0 } - filesAdd = append(filesAdd, blockRecordFileFromAPI(file)) + blockRecord := blockRecordFileFromAPI(file) + + // Set the merkle tree info as appropriate. + if !setFileMerkleInfo(&blockRecord) { + EncodeJSON(w, r, apiBlockchainBlockStatus{Status: blockchain.StatusNotInWarehouse}) + return + } + + filesAdd = append(filesAdd, blockRecord) } newHeight, newVersion, status := core.UserBlockchain.ReplaceFiles(filesAdd) @@ -285,3 +316,23 @@ func (info *apiFileMetadata) GetNumber() uint64 { func (file *apiFile) IsVirtualFolder() bool { return file.Type == core.TypeFolder && file.Format == core.FormatFolder } + +// setFileMerkleInfo sets the merkle fields in the BlockRecordFile +func setFileMerkleInfo(file *blockchain.BlockRecordFile) (valid bool) { + if file.Size <= merkle.MinimumFragmentSize { + // If smaller or equal than the minimum fragment size, the merkle tree is not used. + file.MerkleRootHash = file.Hash + file.FragmentSize = merkle.MinimumFragmentSize + } else { + // Get the information from the Warehouse .merkle companion file. + tree, status, _ := core.UserWarehouse.ReadMerkleTree(file.Hash, true) + if status != warehouse.StatusOK { + return false + } + + file.MerkleRootHash = tree.RootHash + file.FragmentSize = tree.FragmentSize + } + + return true +} diff --git a/webapi/Warehouse.go b/webapi/Warehouse.go index defc5f6..d4960ce 100644 --- a/webapi/Warehouse.go +++ b/webapi/Warehouse.go @@ -27,7 +27,7 @@ Request: POST /warehouse/create with raw data to create as new file Response: 200 with JSON structure WarehouseResult */ func apiWarehouseCreateFile(w http.ResponseWriter, r *http.Request) { - hash, status, err := core.UserWarehouse.CreateFile(r.Body) + hash, status, err := core.UserWarehouse.CreateFile(r.Body, 0) if err != nil { core.Filters.LogError("warehouse.CreateFile", "status %d error: %v", status, err) diff --git a/webapi/readme.md b/webapi/readme.md index 3c0d5a3..188546a 100644 --- a/webapi/readme.md +++ b/webapi/readme.md @@ -229,7 +229,7 @@ type apiFile struct { Name string `json:"name"` // Name of the file Description string `json:"description"` // Description. This is expected to be multiline and contain hashtags! Date time.Time `json:"date"` // Date shared - NodeID []byte `json:"nodeid"` // Node ID, owner of the file + NodeID []byte `json:"nodeid"` // Node ID, owner of the file. Read only. Metadata []apiFileMetadata `json:"metadata"` // Additional metadata. } @@ -262,7 +262,8 @@ This adds a file with the provided information to the blockchain. The date field Any file added is publicly accessible. The user should be informed about this fact in advance. The user is responsible and liable for any files shared. -Each file must be already stored in the Warehouse (virtual folders are exempt). If any file is not stored in the Warehouse, the function aborts with the status code StatusNotInWarehouse. Files can be added to the Warehouse via `/warehouse/create` and `/warehouse/create/path`. +Each file must be already stored in the Warehouse (virtual folders are exempt). Files in the Warehouse are identified using the hash. +If any file is not stored in the Warehouse, the function aborts with the status code StatusNotInWarehouse. Files can be added to the Warehouse via `/warehouse/create` and `/warehouse/create/path`. If the block record encoding fails for any file, this function aborts with the status code StatusCorruptBlockRecord. In case the function aborts, the blockchain remains unchanged. @@ -945,6 +946,9 @@ Status codes: | 11 | StatusErrorReadFile | Error reading file. | | 12 | StatusErrorSeekFile | Error seeking to position in file. | | 13 | StatusErrorTargetExists | Target file already exists. | +| 14 | StatusErrorCreateTarget | Error creating target file. | +| 15 | StatusErrorCreateMerkle | Error creating merkle tree. | +| 16 | StatusErrorMerkleTreeFile | Invalid merkle tree companion file. | ### Create File