From 6c399a7be93cac6d71d66b51fff0a51975eddfc6 Mon Sep 17 00:00:00 2001 From: Kleissner Date: Thu, 19 Aug 2021 16:49:37 +0200 Subject: [PATCH] New function UserBlockchainListFiles to list all files in the users blockchain. Refactor block record decoding code. --- Block Records.go | 83 ++++++++++++++++++++++++++++++++++-------------- Blockchain.go | 32 +++++++++++++++++++ 2 files changed, 91 insertions(+), 24 deletions(-) diff --git a/Block Records.go b/Block Records.go index b65c54b..34598d1 100644 --- a/Block Records.go +++ b/Block Records.go @@ -4,6 +4,24 @@ Copyright: 2021 Peernet s.r.o. Author: Peter Kleissner Encoding of records inside blocks. + +File records: +Offset Size Info +0 32 Hash blake3 of the file content +32 1 File type (low-level) +33 2 File format (high-level) +35 8 File size +43 2 Directory ID +45 2 Size of file name in bytes +47 2 Count of tags +49 ? File name +? ? Tags (TBD) + +Directory records: +Offset Size Info +0 2 Directory ID +2 ? Directory name + */ package core @@ -66,27 +84,53 @@ type BlockRecordFile struct { func decodeBlockRecords(block *Block) (decoded *BlockDecoded, err error) { decoded = &BlockDecoded{Block: *block} - for _, record := range block.RecordsRaw { + if decoded.Files, decoded.directories, err = decodeBlockRecordFiles(block.RecordsRaw); err != nil { + return nil, err + } + if user := decodeBlockRecordUser(block.RecordsRaw); user != nil { + decoded.User = *user + } + return decoded, nil +} + +// decodeBlockRecordUser decodes the username, if available. Otherwise return nil. +func decodeBlockRecordUser(recordsRaw []BlockRecordRaw) (user *BlockRecordUser) { + for _, record := range recordsRaw { + if record.Type == RecordTypeUsername { + user = &BlockRecordUser{Valid: true, Name: string(record.Data), NameS: sanitize.Username(string(record.Data))} + // continue to seek for an overriding username record, even though that would be stupid within the same block + } + } + return +} + +// encodeBlockRecordUser encodes the username +func encodeBlockRecordUser(user BlockRecordUser) (recordsRaw []BlockRecordRaw, err error) { + if user.Valid { + recordsRaw = append(recordsRaw, BlockRecordRaw{Type: RecordTypeUsername, Data: []byte(user.Name)}) + } + + return recordsRaw, nil +} + +// decodeBlockRecordFiles decodes only file records. Other records are ignored +func decodeBlockRecordFiles(recordsRaw []BlockRecordRaw) (files []BlockRecordFile, directories []BlockRecordDirectory, err error) { + for _, record := range recordsRaw { switch record.Type { - case RecordTypeUsername: - decoded.User.Name = string(record.Data) - decoded.User.NameS = sanitize.Username(decoded.User.Name) - decoded.User.Valid = true - case RecordTypeDirectory: if len(record.Data) < 3 { - return nil, errors.New("decodeBlockRecord directory record invalid size") + return nil, nil, errors.New("decodeBlockRecordFiles directory record invalid size") } directory := &BlockRecordDirectory{} directory.ID = binary.LittleEndian.Uint16(record.Data[0 : 0+2]) directory.Name = string(record.Data[2:]) - decoded.directories = append(decoded.directories, *directory) + directories = append(directories, *directory) case RecordTypeFile: if len(record.Data) < 49 { - return nil, errors.New("decodeBlockRecord file record invalid size") + return nil, nil, errors.New("decodeBlockRecordFiles file record invalid size") } file := BlockRecordFile{} @@ -100,31 +144,22 @@ func decodeBlockRecords(block *Block) (decoded *BlockDecoded, err error) { //countTags := binary.LittleEndian.Uint16(record.Data[47 : 47+2]) // future implementation of tags if len(record.Data) < 49+int(filenameSize) { - return nil, errors.New("decodeBlockRecord file record invalid filename size") + return nil, nil, errors.New("decodeBlockRecordFiles file record invalid filename size") } file.Name = string(record.Data[49 : 49+filenameSize]) - for n := range decoded.directories { - if decoded.directories[n].ID == directoryID { - file.Directory = decoded.directories[n].Name + for n := range directories { + if directories[n].ID == directoryID { + file.Directory = directories[n].Name break } } - decoded.Files = append(decoded.Files, file) + files = append(files, file) } } - return decoded, nil -} - -// encodeBlockRecordUser encodes the username -func encodeBlockRecordUser(user BlockRecordUser) (recordsRaw []BlockRecordRaw, err error) { - if user.Valid { - recordsRaw = append(recordsRaw, BlockRecordRaw{Type: RecordTypeUsername, Data: []byte(user.Name)}) - } - - return recordsRaw, nil + return files, directories, err } // encodeBlockRecordFiles encodes files into the block record data diff --git a/Blockchain.go b/Blockchain.go index 2dbded5..75350d3 100644 --- a/Blockchain.go +++ b/Blockchain.go @@ -223,3 +223,35 @@ func UserBlockchainAddFiles(files []BlockRecordFile) (newHeight uint64, status i return UserBlockchainAppend(encoded) } + +// UserBlockchainListFiles returns a list of all files +// If there is a corruption in the blockchain it will reading it but return the files parsed so far. +// Status: 0 = Success, 1 = Block not found, 2 = Error block encoding, 3 = Error block record encoding +func UserBlockchainListFiles() (files []BlockRecordFile, status int) { + // TODO: Add internal cache of file list for faster subsequent processing? + height := userBlockchainHeader.height + + // read all blocks until height is reached + for blockN := uint64(0); blockN < height; blockN++ { + var target [8]byte + binary.LittleEndian.PutUint64(target[:], userBlockchainHeader.height-1) + blockRaw, found := userBlockchainDB.Get(target[:]) + if !found || len(blockRaw) == 0 { + return files, 1 + } + + block, err := decodeBlock(blockRaw) + if err != nil { + return files, 2 + } + + filesMore, _, err := decodeBlockRecordFiles(block.RecordsRaw) + if err != nil { + return nil, 3 + } + + files = append(files, filesMore...) + } + + return files, 0 +}