// contentMetadata provides functions to store and retrieve metadata about profiles and messages // Content metadata is stored in the database, so the content can be deleted and the metadata can still be retained package contentMetadata // Storing content metadata in the database enables: // 1. We do not have to calculate the ProfileIsCanonical status and the attribute hashes for the same profile more than once // 2. Hosts/Moderators can delete banned messages and still keep the cipher key hash to verify reviews for the messages // 3. Hosts can delete banned profiles/messages and still be aware if those profile's/message's reviews are within their range // Knowing if a profile's/message's reviews are within your range requires knowing the profile author/message inbox // 4. Hosts can delete banned profiles and still be aware of why those profiles were banned (if their attributes were banned) // We need to keep track of the profile's attribute hashes to be able to determine the profile's verdict consensus // 5. It is faster to retrieve metadata than to read it from the profile/message // This is because we do not need to read the entire message/profile into memory // Profile metadata: // -Profile version // -Profile network type // -Profile identity Hash // -Profile creation time // -Profile is disabled status // -Profile is canonical status // -Profile attribute hashes map // Message metadata: // -Message version // -Message network type // -Message size in bytes // -Message Inbox // -Message Cipher key hash import "seekia/internal/encoding" import "seekia/internal/badgerDatabase" import "seekia/internal/profiles/readProfiles" import "seekia/internal/messaging/readMessages" import messagepack "github.com/vmihailenco/msgpack/v5" import "errors" //Outputs: // -bool: Profile metadata exists // -int: Profile Version // -byte: Profile network type (1 == Mainnet, 2 == Testnet1) // -[16]byte: Profile author identity Hash // -int64: Profile creation time // -bool: Profile is disabled // -bool: Profile is canonical // -map[int][27]byte: Map of Attribute identifier -> Attribute hash // -error func GetProfileMetadata(profileHash [28]byte)(bool, int, byte, [16]byte, int64, bool, bool, map[int][27]byte, error){ metadataExists, profileMetadata, err := badgerDatabase.GetProfileMetadata(profileHash) if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err } if (metadataExists == true){ type profileMetadataStruct struct{ ProfileVersion int NetworkType byte IdentityHash [16]byte CreationTime int64 IsDisabled bool IsCanonical bool AttributeHashesMap map[int][27]byte } var profileMetadataObject profileMetadataStruct err := encoding.DecodeMessagePackBytes(false, profileMetadata, &profileMetadataObject) if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, errors.New("Database malformed: contains invalid profile metadata: " + err.Error()) } profileVersion := profileMetadataObject.ProfileVersion profileNetworkType := profileMetadataObject.NetworkType profileIdentityHash := profileMetadataObject.IdentityHash profileCreationTime := profileMetadataObject.CreationTime profileIsDisabled := profileMetadataObject.IsDisabled profileIsCanonical := profileMetadataObject.IsCanonical attributeHashesMap := profileMetadataObject.AttributeHashesMap if (profileIsDisabled == true){ emptyMap := make(map[int][27]byte) return true, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, true, true, emptyMap, nil } return true, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, false, profileIsCanonical, attributeHashesMap, nil } profileType, _, err := readProfiles.ReadProfileHashMetadata(profileHash) if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err } profileExists, profileBytes, err := badgerDatabase.GetUserProfile(profileType, profileHash) if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err } if (profileExists == false){ return false, 0, 0, [16]byte{}, 0, false, false, nil, nil } ableToRead, profileHash_Retrieved, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, profileIsDisabled, rawProfileMap, err := readProfiles.ReadProfileAndHash(false, profileBytes) if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err } if (ableToRead == false){ return false, 0, 0, [16]byte{}, 0, false, false, nil, errors.New("Database corrupt: Contains invalid profile: " + err.Error()) } if (profileHash != profileHash_Retrieved){ return false, 0, 0, [16]byte{}, 0, false, false, nil, errors.New("Database corrupt: Profile hash does not match profile entry key") } profileAttributeHashesMap, profileIsCanonical, err := readProfiles.GetProfileAttributeHashesMap(profileIdentityHash, profileVersion, profileNetworkType, rawProfileMap) if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err } profileVersionEncoded, err := encoding.EncodeMessagePackBytes(profileVersion) if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err } profileNetworkTypeEncoded, err := encoding.EncodeMessagePackBytes(profileNetworkType) if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err } identityHashEncoded, err := encoding.EncodeMessagePackBytes(profileIdentityHash) if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err } creationTimeEncoded, err := encoding.EncodeMessagePackBytes(profileCreationTime) if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err } isDisabledEncoded, err := encoding.EncodeMessagePackBytes(profileIsDisabled) if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err } isCanonicalEncoded, err := encoding.EncodeMessagePackBytes(profileIsCanonical) if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err } attributeHashesMapEncoded, err := encoding.EncodeMessagePackBytes(profileAttributeHashesMap) if (err != nil){ return false, 0, 0, [16]byte{}, 0, false, false, nil, err } profileMetadataSlice := []messagepack.RawMessage{profileVersionEncoded, profileNetworkTypeEncoded, identityHashEncoded, creationTimeEncoded, isDisabledEncoded, isCanonicalEncoded, attributeHashesMapEncoded} profileMetadataBytes, err := encoding.EncodeMessagePackBytes(profileMetadataSlice) if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err } err = badgerDatabase.AddProfileMetadata(profileHash, profileMetadataBytes) if (err != nil) { return false, 0, 0, [16]byte{}, 0, false, false, nil, err } return true, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, profileIsDisabled, profileIsCanonical, profileAttributeHashesMap, nil } //Outputs: // -bool: Message metadata exists // -int: Message version // -byte: Message network type (1 == Mainnet, 2 == Testnet1) // -int: Message size in bytes // -[10]byte: Message inbox // -[25]byte: Message cipher key hash // -error func GetMessageMetadata(messageHash [26]byte)(bool, int, byte, int, [10]byte, [25]byte, error){ exists, messageMetadata, err := badgerDatabase.GetMessageMetadata(messageHash) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } if (exists == true){ type messageMetadataStruct struct{ MessageVersion int MessageNetworkType byte MessageSize int MessageInbox [10]byte MessageCipherKeyHash [25]byte } var messageMetadataObject messageMetadataStruct err := encoding.DecodeMessagePackBytes(false, messageMetadata, &messageMetadataObject) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, errors.New("Database malformed: contains invalid message metadata: " + err.Error()) } messageVersion := messageMetadataObject.MessageVersion messageNetworkType := messageMetadataObject.MessageNetworkType messageSize := messageMetadataObject.MessageSize messageInbox := messageMetadataObject.MessageInbox messageCipherKeyHash := messageMetadataObject.MessageCipherKeyHash return true, messageVersion, messageNetworkType, messageSize, messageInbox, messageCipherKeyHash, nil } messageExists, messageBytes, err := badgerDatabase.GetChatMessage(messageHash) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } if (messageExists == false){ return false, 0, 0, 0, [10]byte{}, [25]byte{}, nil } ableToRead, messageHash_Retrieved, messageVersion, messageNetworkType, messageInbox, _, _, _, messageCipherKeyHash, _, _, err := readMessages.ReadChatMessagePublicDataAndHash(false, messageBytes) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } if (ableToRead == false){ return false, 0, 0, 0, [10]byte{}, [25]byte{}, errors.New("Database corrupt: Contains invalid message.") } if (messageHash != messageHash_Retrieved){ return false, 0, 0, 0, [10]byte{}, [25]byte{}, errors.New("Database corrupt: Chat message entry key does not match message hash.") } messageSize := len(messageBytes) messageVersionEncoded, err := encoding.EncodeMessagePackBytes(messageVersion) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } messageNetworkTypeEncoded, err := encoding.EncodeMessagePackBytes(messageNetworkType) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } messageSizeEncoded, err := encoding.EncodeMessagePackBytes(messageSize) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } messageInboxEncoded, err := encoding.EncodeMessagePackBytes(messageInbox) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } messageCipherKeyHashEncoded, err := encoding.EncodeMessagePackBytes(messageCipherKeyHash) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } messageSlice := []messagepack.RawMessage{messageVersionEncoded, messageNetworkTypeEncoded, messageSizeEncoded, messageInboxEncoded, messageCipherKeyHashEncoded} newMessageMetadata, err := encoding.EncodeMessagePackBytes(messageSlice) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } err = badgerDatabase.AddMessageMetadata(messageHash, newMessageMetadata) if (err != nil) { return false, 0, 0, 0, [10]byte{}, [25]byte{}, err } return true, messageVersion, messageNetworkType, messageSize, messageInbox, messageCipherKeyHash, nil }