diff --git a/search/Search NodeIDs Based on Hash.go b/search/Search NodeIDs Based on Hash.go new file mode 100644 index 0000000..c946630 --- /dev/null +++ b/search/Search NodeIDs Based on Hash.go @@ -0,0 +1,24 @@ +package search + +import ( + "github.com/PeernetOfficial/core/protocol" + "github.com/google/uuid" +) + +// SearchNodeIDBasedOnHash Provides a list of NodeIDs +// based on the hash provided +// This is used to find out which nodes are hosting +// which files based on the hash provided +func (index *SearchIndexStore) SearchNodeIDBasedOnHash(hash []byte) (NodeIDs [][]byte, err error) { + var resultMap map[uuid.UUID]*SearchIndexRecord + err = index.LookupHash(SearchSelector{Hash: hash}, resultMap) + if err != nil { + return + } + + for i := range resultMap { + NodeIDs = append(NodeIDs, protocol.PublicKey2NodeID(resultMap[i].PublicKey)) + } + + return +} diff --git a/webapi/API.go b/webapi/API.go index 9b4f9f2..319dbbb 100644 --- a/webapi/API.go +++ b/webapi/API.go @@ -81,6 +81,7 @@ func Start(Backend *core.Backend, ListenAddresses []string, UseSSL bool, Certifi api.Router.HandleFunc("/blockchain/file/delete", api.apiBlockchainFileDelete).Methods("POST") api.Router.HandleFunc("/blockchain/file/update", api.apiBlockchainFileUpdate).Methods("POST") api.Router.HandleFunc("/blockchain/view", api.apiExploreNodeID).Methods("GET") + api.Router.HandleFunc("/merge/directory", api.apiMergeDirectory).Methods("GET") api.Router.HandleFunc("/profile/list", api.apiProfileList).Methods("GET") api.Router.HandleFunc("/profile/read", api.apiProfileRead).Methods("GET") api.Router.HandleFunc("/profile/write", api.apiProfileWrite).Methods("POST") diff --git a/webapi/Blockchain.go b/webapi/Blockchain.go index ae6e6e3..ac7db19 100644 --- a/webapi/Blockchain.go +++ b/webapi/Blockchain.go @@ -7,18 +7,17 @@ Author: Peter Kleissner package webapi import ( - "bytes" "encoding/hex" "net/http" "strconv" - "github.com/PeernetOfficial/core/blockchain" + "github.com/PeernetOfficial/core/blockchain" ) type apiBlockchainHeader struct { - PeerID string `json:"peerid"` // Peer ID hex encoded. - Version uint64 `json:"version"` // Current version number of the blockchain. - Height uint64 `json:"height"` // Height of the blockchain (number of blocks). If 0, no data exists. + PeerID string `json:"peerid"` // Peer ID hex encoded. + Version uint64 `json:"version"` // Current version number of the blockchain. + Height uint64 `json:"height"` // Height of the blockchain (number of blocks). If 0, no data exists. } /* @@ -28,25 +27,25 @@ Request: GET /blockchain/header Result: 200 with JSON structure apiResponsePeerSelf */ func (api *WebapiInstance) apiBlockchainHeaderFunc(w http.ResponseWriter, r *http.Request) { - publicKey, height, version := api.Backend.UserBlockchain.Header() + publicKey, height, version := api.Backend.UserBlockchain.Header() - EncodeJSON(api.Backend, w, r, apiBlockchainHeader{Version: version, Height: height, PeerID: hex.EncodeToString(publicKey.SerializeCompressed())}) + EncodeJSON(api.Backend, w, r, apiBlockchainHeader{Version: version, Height: height, PeerID: hex.EncodeToString(publicKey.SerializeCompressed())}) } type apiBlockRecordRaw struct { - Type uint8 `json:"type"` // Record Type. See core.RecordTypeX. - Data []byte `json:"data"` // Data according to the type. + Type uint8 `json:"type"` // Record Type. See core.RecordTypeX. + Data []byte `json:"data"` // Data according to the type. } // apiBlockchainBlockRaw contains a raw block of the blockchain via API type apiBlockchainBlockRaw struct { - Records []apiBlockRecordRaw `json:"records"` // Block records in encoded raw format. + Records []apiBlockRecordRaw `json:"records"` // Block records in encoded raw format. } type apiBlockchainBlockStatus struct { - Status int `json:"status"` // See blockchain.StatusX. - Height uint64 `json:"height"` // Height of the blockchain (number of blocks). - Version uint64 `json:"version"` // Version of the blockchain. + Status int `json:"status"` // See blockchain.StatusX. + Height uint64 `json:"height"` // Height of the blockchain (number of blocks). + Version uint64 `json:"version"` // Version of the blockchain. } /* @@ -57,30 +56,30 @@ Request: POST /blockchain/append with JSON structure apiBlockchainBlockRaw Response: 200 with JSON structure apiBlockchainBlockStatus */ func (api *WebapiInstance) apiBlockchainAppend(w http.ResponseWriter, r *http.Request) { - var input apiBlockchainBlockRaw - if err := DecodeJSON(w, r, &input); err != nil { - return - } + var input apiBlockchainBlockRaw + if err := DecodeJSON(w, r, &input); err != nil { + return + } - var records []blockchain.BlockRecordRaw + var records []blockchain.BlockRecordRaw - for _, record := range input.Records { - records = append(records, blockchain.BlockRecordRaw{Type: record.Type, Data: record.Data}) - } + for _, record := range input.Records { + records = append(records, blockchain.BlockRecordRaw{Type: record.Type, Data: record.Data}) + } - newHeight, newVersion, status := api.Backend.UserBlockchain.Append(records) + newHeight, newVersion, status := api.Backend.UserBlockchain.Append(records) - EncodeJSON(api.Backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion}) + EncodeJSON(api.Backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion}) } type apiBlockchainBlock struct { - Status int `json:"status"` // See blockchain.StatusX. - PeerID string `json:"peerid"` // Peer ID hex encoded. - LastBlockHash []byte `json:"lastblockhash"` // Hash of the last block. Blake3. - BlockchainVersion uint64 `json:"blockchainversion"` // Blockchain version - Number uint64 `json:"blocknumber"` // Block number - RecordsRaw []apiBlockRecordRaw `json:"recordsraw"` // Records raw. Successfully decoded records are parsed into the below fields. - RecordsDecoded []interface{} `json:"recordsdecoded"` // Records decoded. The encoding for each record depends on its type. + Status int `json:"status"` // See blockchain.StatusX. + PeerID string `json:"peerid"` // Peer ID hex encoded. + LastBlockHash []byte `json:"lastblockhash"` // Hash of the last block. Blake3. + BlockchainVersion uint64 `json:"blockchainversion"` // Blockchain version + Number uint64 `json:"blocknumber"` // Block number + RecordsRaw []apiBlockRecordRaw `json:"recordsraw"` // Records raw. Successfully decoded records are parsed into the below fields. + RecordsDecoded []interface{} `json:"recordsdecoded"` // Records decoded. The encoding for each record depends on its type. } /* @@ -90,63 +89,36 @@ Request: GET /blockchain/read?block=[number] Result: 200 with JSON structure apiBlockchainBlock */ func (api *WebapiInstance) apiBlockchainRead(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - blockN, err := strconv.Atoi(r.Form.Get("block")) - if err != nil || blockN < 0 { - http.Error(w, "", http.StatusBadRequest) - return - } + r.ParseForm() + blockN, err := strconv.Atoi(r.Form.Get("block")) + if err != nil || blockN < 0 { + http.Error(w, "", http.StatusBadRequest) + return + } - block, status, _ := api.Backend.UserBlockchain.Read(uint64(blockN)) - result := apiBlockchainBlock{Status: status} + block, status, _ := api.Backend.UserBlockchain.Read(uint64(blockN)) + result := apiBlockchainBlock{Status: status} - if status == 0 { - for _, record := range block.RecordsRaw { - result.RecordsRaw = append(result.RecordsRaw, apiBlockRecordRaw{Type: record.Type, Data: record.Data}) - } + if status == 0 { + for _, record := range block.RecordsRaw { + result.RecordsRaw = append(result.RecordsRaw, apiBlockRecordRaw{Type: record.Type, Data: record.Data}) + } - result.PeerID = hex.EncodeToString(block.OwnerPublicKey.SerializeCompressed()) + result.PeerID = hex.EncodeToString(block.OwnerPublicKey.SerializeCompressed()) - for _, record := range block.RecordsDecoded { - switch v := record.(type) { - case blockchain.BlockRecordFile: - result.RecordsDecoded = append(result.RecordsDecoded, blockRecordFileToAPI(v)) + for _, record := range block.RecordsDecoded { + switch v := record.(type) { + case blockchain.BlockRecordFile: + result.RecordsDecoded = append(result.RecordsDecoded, blockRecordFileToAPI(v)) - case blockchain.BlockRecordProfile: - result.RecordsDecoded = append(result.RecordsDecoded, blockRecordProfileToAPI(v)) + case blockchain.BlockRecordProfile: + result.RecordsDecoded = append(result.RecordsDecoded, blockRecordProfileToAPI(v)) - } - } - } + } + } + } - EncodeJSON(api.Backend, w, r, result) -} - -/* -apiExploreNodeID returns the shared files of a particular node in Peernet. Results are returned in real-time. The file type is an optional filter. See TypeX. -Special type -2 = Binary, Compressed, Container, Executable. This special type includes everything except Documents, Video, Audio, Ebooks, Picture, Text. - -Request: GET /blockchain/view?limit=[max records]&type=[file type]&offset=[offset]&node=[node id] -Result: 200 with JSON structure SearchResult. Check the field status. -*/ -func (api *WebapiInstance) apiExploreNodeID(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - offset, _ := strconv.Atoi(r.Form.Get("offset")) - limit, err := strconv.Atoi(r.Form.Get("limit")) - if err != nil { - limit = 100 - } - // ID fields for results for a specific node ID. - NodeId, _ := DecodeBlake3Hash(r.Form.Get("node")) - - fileType, err := strconv.Atoi(r.Form.Get("type")) - if err != nil { - fileType = -1 - } - - result := api.ExploreHelper(fileType, limit, offset, NodeId, true) - - EncodeJSON(api.Backend, w, r, result) + EncodeJSON(api.Backend, w, r, result) } /* @@ -171,14 +143,6 @@ func (api *WebapiInstance) apiExploreNodeID(w http.ResponseWriter, r *http.Reque fileType = -1 } - // check if the node IDs are the same - if bytes.Compare(NodeId, api.Backend.SelfNodeID()) == 0 { - // setting the value of block to 0 - r.Form.Add("block", "0") - api.apiBlockchainRead(w, r) - return - } - result := api.ExploreHelper(fileType, limit, offset, NodeId, true) EncodeJSON(api.Backend, w, r, result) diff --git a/webapi/Merge directory.go b/webapi/Merge directory.go new file mode 100644 index 0000000..8278f76 --- /dev/null +++ b/webapi/Merge directory.go @@ -0,0 +1,62 @@ +package webapi + +import ( + "net/http" + "strconv" +) + +// SearchResultMergedDirectory contains results for the merged directory. +type SearchResultMergedDirectory struct { + Status int `json:"status"` // Status: 0 = Success with results, 1 = No more results available, 2 = Search ID not found, 3 = No results yet available keep trying + Files [][]apiFile `json:"files"` // List of files found + Statistic interface{} `json:"statistic"` // Statistics of all results (independent from applied filters), if requested. Only set if files are returned (= if statistics changed). See SearchStatisticData. +} + +func (api *WebapiInstance) apiMergeDirectory(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + offset, _ := strconv.Atoi(r.Form.Get("offset")) + limit, err := strconv.Atoi(r.Form.Get("limit")) + if err != nil { + limit = 100 + } + // ID fields for results for a specific node ID. + NodeId, _ := DecodeBlake3Hash(r.Form.Get("node")) + hash, _ := DecodeBlake3Hash(r.Form.Get("hash")) + + fileType, err := strconv.Atoi(r.Form.Get("type")) + if err != nil { + fileType = -1 + } + + result := api.ExploreFileSharedByNodeThatSharedSimilarFile(fileType, limit, offset, NodeId, hash, true) + + EncodeJSON(api.Backend, w, r, result) +} + +// ExploreFileSharedByNodeThatSharedSimilarFile lists files shared by a nodes which share the same file as a common point +// Currently this is a greedy search and requires work for optimization +func (api *WebapiInstance) ExploreFileSharedByNodeThatSharedSimilarFile(fileType int, limit, offset int, nodeID []byte, hash []byte, nodeIDState bool) *SearchResultMergedDirectory { + // lookup all NodeID blockchains which have the similar hash + // do a search to get all the node IDs sharing the particular file + NodeIDs, _ := api.Backend.SearchIndex.SearchNodeIDBasedOnHash(nodeID) + + var result SearchResultMergedDirectory + + for i := range NodeIDs { + resultFiles := api.queryRecentShared(api.Backend, fileType, uint64(limit*20/100), uint64(offset), uint64(limit), NodeIDs[i], nodeIDState) + + var ApiFile []apiFile + + // loop over results + for n := range resultFiles { + ApiFile = append(ApiFile, blockRecordFileToAPI(resultFiles[n])) + } + + result.Files = append(result.Files, ApiFile) + + } + + result.Status = 1 // No more results to expect + + return &result +}