From b21e97728579b2b102083a914592bd7db1e6a84e Mon Sep 17 00:00:00 2001 From: Kleissner Date: Fri, 27 Aug 2021 17:50:07 +0200 Subject: [PATCH] Update API to reflect latest core library. /blockchain functions use changed JSON structures (apiBlockRecordFile, apiBlockRecordProfile). #1 --- API.go | 158 +++++++++++++++++++++++++++++++++++++++++++++++---------- API.md | 54 +++++++++++++------- go.mod | 3 +- go.sum | 6 ++- 4 files changed, 172 insertions(+), 49 deletions(-) diff --git a/API.go b/API.go index f4c5601..224a166 100644 --- a/API.go +++ b/API.go @@ -18,6 +18,7 @@ import ( "time" "github.com/PeernetOfficial/core" + "github.com/google/uuid" "github.com/gorilla/mux" "github.com/gorilla/websocket" ) @@ -280,28 +281,51 @@ type apiBlockchainBlock struct { 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"` // Block records raw. Successfully decoded records are parsed into the below fields. - Decoded struct { - User apiBlockRecordUser `json:"user"` // User details - Files []apiBlockRecordFile `json:"files"` // Files - } `json:"decoded"` + 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. } -// apiBlockRecordUser specifies user information -type apiBlockRecordUser struct { - NameValid bool `json:"namevalid"` // Whether the name is supplied in this structure. - Name string `json:"name"` // Arbitrary name of the user. - NameSanitized string `json:"namesanitized"` // Sanitized version of the name. +// apiBlockRecordProfile contains end-user information. Any data is treated as untrusted and unverified by default. +type apiBlockRecordProfile struct { + Fields []apiBlockRecordProfileField `json:"fields"` // All fields + Blobs []apiBlockRecordProfileBlob `json:"blobs"` // Blobs +} + +// apiBlockRecordProfileField contains a single information about the end user. The data is always UTF8 text encoded. +// Note that all profile data is arbitrary and shall be considered untrusted and unverified. +// To establish trust, the user must load Certificates into the blockchain that validate certain data. +type apiBlockRecordProfileField struct { + Type uint16 `json:"type"` // See ProfileFieldX constants. + Text string `json:"text"` // The data +} + +// apiBlockRecordProfileBlob is similar to apiBlockRecordProfileField but contains binary objects instead of text. +// It can be used for example to store a profile picture on the blockchain. +type apiBlockRecordProfileBlob struct { + Type uint16 `json:"type"` // See ProfileBlobX constants. + Data []byte `json:"data"` // The data +} + +// apiFileTag describes a generic tag. +// Note that not all tags may be able to be decoded into this generic structure! +// This structure exists for known (defined) tags that do not have a fixed field in the apiBlockRecordFile structure. +// The blockchain supports not yet (future/custom) defined tags, which are accessible via the core.UserBlockchainX functions. +type apiFileTag struct { + Key string `json:"key"` // Name of the tag + Text string `json:"text"` // Text value of the tag } // apiBlockRecordFile is the metadata of a file published on the blockchain type apiBlockRecordFile struct { - Hash []byte `json:"hash"` // Blake3 hash of the file data - Type uint8 `json:"type"` // Type (low-level) - Format uint16 `json:"format"` // Format (high-level) - Size uint64 `json:"size"` // Size of the file - Directory string `json:"directory"` // Directory - Name string `json:"name"` // File name + ID uuid.UUID `json:"id"` // Unique ID. + Hash []byte `json:"hash"` // Blake3 hash of the file data + Type uint8 `json:"type"` // Type (low-level) + Format uint16 `json:"format"` // Format (high-level) + Size uint64 `json:"size"` // Size of the file + Folder string `json:"folder"` // Folder, optional + Name string `json:"name"` // Name of the file + Description string `json:"description"` // Description. This is expected to be multiline and contain hashtags! + Tags []apiFileTag `json:"tags"` // Tags } /* @@ -318,7 +342,7 @@ func apiBlockchainSelfRead(w http.ResponseWriter, r *http.Request) { return } - block, status := core.UserBlockchainRead(uint64(blockN)) + block, status, _ := core.UserBlockchainRead(uint64(blockN)) result := apiBlockchainBlock{Status: status} if status == 0 { @@ -328,14 +352,15 @@ func apiBlockchainSelfRead(w http.ResponseWriter, r *http.Request) { result.PeerID = hex.EncodeToString(block.OwnerPublicKey.SerializeCompressed()) - if block.User.Valid { - result.Decoded.User.NameValid = true - result.Decoded.User.Name = block.User.Name - result.Decoded.User.NameSanitized = block.User.NameS - } + for _, record := range block.RecordsDecoded { + switch v := record.(type) { + case core.BlockRecordFile: + result.RecordsDecoded = append(result.RecordsDecoded, blockRecordFileToAPI(v)) - for _, file := range block.Files { - result.Decoded.Files = append(result.Decoded.Files, apiBlockRecordFile{Hash: file.Hash, Type: file.Type, Format: file.Format, Size: file.Size, Directory: file.Directory, Name: file.Name}) + case core.BlockRecordProfile: + result.RecordsDecoded = append(result.RecordsDecoded, blockRecordProfileToAPI(v)) + + } } } @@ -360,13 +385,17 @@ func apiBlockchainSelfAddFile(w http.ResponseWriter, r *http.Request) { return } - var files []core.BlockRecordFile + var filesAdd []core.BlockRecordFile for _, file := range input.Files { - files = append(files, core.BlockRecordFile{Hash: file.Hash, Type: file.Type, Format: file.Format, Size: file.Size, Directory: file.Directory, Name: file.Name}) + if file.ID == uuid.Nil { // if the ID is not provided by the caller, set it + file.ID = uuid.New() + } + + filesAdd = append(filesAdd, blockRecordFileFromAPI(file)) } - newHeight, status := core.UserBlockchainAddFiles(files) + newHeight, status := core.UserBlockchainAddFiles(filesAdd) apiEncodeJSON(w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight}) } @@ -383,10 +412,83 @@ func apiBlockchainSelfListFile(w http.ResponseWriter, r *http.Request) { var result apiBlockAddFiles for _, file := range files { - result.Files = append(result.Files, apiBlockRecordFile{Hash: file.Hash, Type: file.Type, Format: file.Format, Size: file.Size, Directory: file.Directory, Name: file.Name}) + result.Files = append(result.Files, blockRecordFileToAPI(file)) } result.Status = status apiEncodeJSON(w, r, result) } + +// --- conversion from core to API data --- + +func blockRecordFileToAPI(input core.BlockRecordFile) (output apiBlockRecordFile) { + output = apiBlockRecordFile{ID: input.ID, Hash: input.Hash, Type: input.Type, Format: input.Format, Size: input.Size} + output.Tags = []apiFileTag{} + + for _, tagDecoded := range input.TagsDecoded { + switch v := tagDecoded.(type) { + case core.FileTagName: + output.Name = v.Name + + case core.FileTagFolder: + output.Folder = v.Name + + case core.FileTagDescription: + output.Description = v.Description + + case core.FileTagCreated: + output.Tags = append(output.Tags, apiFileTag{Key: "created", Text: v.Date.Format(dateFormat)}) + + } + } + + return output +} + +func blockRecordFileFromAPI(input apiBlockRecordFile) (output core.BlockRecordFile) { + output = core.BlockRecordFile{ID: input.ID, Hash: input.Hash, Type: input.Type, Format: input.Format, Size: input.Size} + + if input.Name != "" { + output.TagsDecoded = append(output.TagsDecoded, core.FileTagName{Name: input.Name}) + } + if input.Folder != "" { + output.TagsDecoded = append(output.TagsDecoded, core.FileTagFolder{Name: input.Folder}) + } + if input.Description != "" { + output.TagsDecoded = append(output.TagsDecoded, core.FileTagDescription{Description: input.Description}) + } + + for _, tag := range input.Tags { + switch tag.Key { + case "created": + if dateF, err := time.Parse(dateFormat, tag.Text); err == nil { + output.TagsDecoded = append(output.TagsDecoded, core.FileTagCreated{Date: dateF}) + } + } + } + + return output +} + +func blockRecordProfileToAPI(input core.BlockRecordProfile) (output apiBlockRecordProfile) { + for n := range input.Fields { + output.Fields = append(output.Fields, apiBlockRecordProfileField{Type: input.Fields[n].Type, Text: input.Fields[n].Text}) + } + for n := range input.Blobs { + output.Blobs = append(output.Blobs, apiBlockRecordProfileBlob{Type: input.Blobs[n].Type, Data: input.Blobs[n].Data}) + } + + return output +} + +func blockRecordProfileFromAPI(input apiBlockRecordProfile) (output core.BlockRecordProfile) { + for n := range input.Fields { + output.Fields = append(output.Fields, core.BlockRecordProfileField{Type: input.Fields[n].Type, Text: input.Fields[n].Text}) + } + for n := range input.Blobs { + output.Blobs = append(output.Blobs, core.BlockRecordProfileBlob{Type: input.Blobs[n].Type, Data: input.Blobs[n].Data}) + } + + return output +} diff --git a/API.md b/API.md index 59891ad..afe7b3a 100644 --- a/API.md +++ b/API.md @@ -159,26 +159,40 @@ type apiBlockchainBlock struct { 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"` // Block records raw. Successfully decoded records are parsed into the below fields. - decoded struct { - User apiBlockRecordUser `json:"user"` // User details - Files []apiBlockRecordFile `json:"files"` // Files - } `json:"decoded"` + 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. } -type apiBlockRecordUser struct { - NameValid bool `json:"namevalid"` // Whether the name is supplied in this structure. - Name string `json:"name"` // Arbitrary name of the user. - NameSanitized string `json:"namesanitized"` // Sanitized version of the name. +type apiBlockRecordProfile struct { + Fields []apiBlockRecordProfileField `json:"fields"` // All fields + Blobs []apiBlockRecordProfileBlob `json:"blobs"` // Blobs +} + +type apiBlockRecordProfileField struct { + Type uint16 `json:"type"` // See ProfileFieldX constants. + Text string `json:"text"` // The data +} + +type apiBlockRecordProfileBlob struct { + Type uint16 `json:"type"` // See ProfileBlobX constants. + Data []byte `json:"data"` // The data +} + +type apiFileTag struct { + Key string `json:"key"` // Name of the tag + Text string `json:"text"` // Text value of the tag } type apiBlockRecordFile struct { - Hash []byte // Hash of the file data - Type uint8 // Type (low-level) - Format uint16 // Format (high-level) - Size uint64 // Size of the file - Directory string // Directory - Name string // File name + ID uuid.UUID `json:"id"` // Unique ID. + Hash []byte `json:"hash"` // Blake3 hash of the file data + Type uint8 `json:"type"` // Type (low-level) + Format uint16 `json:"format"` // Format (high-level) + Size uint64 `json:"size"` // Size of the file + Folder string `json:"folder"` // Folder, optional + Name string `json:"name"` // Name of the file + Description string `json:"description"` // Description. This is expected to be multiline and contain hashtags! + Tags []apiFileTag `json:"tags"` // Tags } ``` @@ -208,12 +222,13 @@ Example POST request data to `http://127.0.0.1:112/blockchain/self/add/file`: ```json { "files": [{ + "id": "236de31d-f402-4389-bdd1-56463abdc309", "hash": "aFad3zRACbk44dsOw5sVGxYmz+Rqh8ORDcGJNqIz+Ss=", "type": 1, "format": 10, "size": 4, "name": "Test.txt", - "directory": "sample directory/sub folder" + "folder": "sample directory/sub folder" }] } ``` @@ -233,12 +248,15 @@ Example output: ```json { "files": [{ + "id": "a59b6465-fe8c-4a61-9fcc-fe37cf711fd4", "hash": "aFad3zRACbk44dsOw5sVGxYmz+Rqh8ORDcGJNqIz+Ss=", "type": 1, "format": 10, "size": 4, - "directory": "sample directory/sub folder", - "name": "Test.txt" + "folder": "sample directory/sub folder", + "name": "Test.txt", + "description": "", + "tags": [] }], "status": 0 } diff --git a/go.mod b/go.mod index f7db058..ba13bfd 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,9 @@ module github.com/PeernetOfficial/Cmd go 1.16 require ( - github.com/PeernetOfficial/core v0.0.0-20210819144937-6c399a7be93c + github.com/PeernetOfficial/core v0.0.0-20210827153836-7c88763f28d5 github.com/btcsuite/btcd v0.22.0-beta.0.20210625194946-86a17263b0ff + github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7 ) diff --git a/go.sum b/go.sum index 0dbdab4..903e2cc 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/PeernetOfficial/core v0.0.0-20210819144937-6c399a7be93c h1:2xOWG1AHIp41IQ1KyIjPUzszkZnKk58izxh27d+1GHk= -github.com/PeernetOfficial/core v0.0.0-20210819144937-6c399a7be93c/go.mod h1:upfaI2iJJ7lyH+UtOLU7t6oVcLO7PrDF7SsSUWJJ/OY= +github.com/PeernetOfficial/core v0.0.0-20210827153836-7c88763f28d5 h1:mJVUgxgBJI/nQ5L2lEsdZBje1PgJ5FAeaLaqboxmdtw= +github.com/PeernetOfficial/core v0.0.0-20210827153836-7c88763f28d5/go.mod h1:GCG3qjbCPEY+/PGIO76Dj/Dp6JvpMYy0B6T+CbmSk1A= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= @@ -24,6 +24,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e h1:8+CIGbDW29nl9niCoMrpF8+ACFz3rLRpIjuqnx4rNKg= github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7 h1:L89uC9ATI61/V2eNgZYtQHyjjyjEplemB+aky4HdyzQ=