github.com/deso-protocol/core@v1.2.9/lib/block_view_message.go (about) 1 package lib 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "github.com/btcsuite/btcd/btcec" 7 "github.com/golang/glog" 8 "github.com/pkg/errors" 9 "reflect" 10 ) 11 12 func (bav *UtxoView) _getMessageEntryForMessageKey(messageKey *MessageKey) *MessageEntry { 13 // If an entry exists in the in-memory map, return the value of that mapping. 14 mapValue, existsMapValue := bav.MessageKeyToMessageEntry[*messageKey] 15 if existsMapValue { 16 return mapValue 17 } 18 19 // If we get here it means no value exists in our in-memory map. In this case, 20 // defer to the db. If a mapping exists in the db, return it. If not, return 21 // nil. Either way, save the value to the in-memory view mapping got later. 22 dbMessageEntry := DbGetMessageEntry(bav.Handle, messageKey.PublicKey[:], messageKey.TstampNanos) 23 if dbMessageEntry != nil { 24 bav._setMessageEntryMappings(dbMessageEntry) 25 } 26 return dbMessageEntry 27 } 28 29 func (bav *UtxoView) _setMessageEntryMappings(messageEntry *MessageEntry) { 30 // This function shouldn't be called with nil. 31 if messageEntry == nil { 32 glog.Errorf("_setMessageEntryMappings: Called with nil MessageEntry; " + 33 "this should never happen.") 34 return 35 } 36 37 // Add a mapping for the sender and the recipient. 38 senderKey := MakeMessageKey(messageEntry.SenderPublicKey, messageEntry.TstampNanos) 39 bav.MessageKeyToMessageEntry[senderKey] = messageEntry 40 41 recipientKey := MakeMessageKey(messageEntry.RecipientPublicKey, messageEntry.TstampNanos) 42 bav.MessageKeyToMessageEntry[recipientKey] = messageEntry 43 } 44 45 func (bav *UtxoView) _deleteMessageEntryMappings(messageEntry *MessageEntry) { 46 47 // Create a tombstone entry. 48 tombstoneMessageEntry := *messageEntry 49 tombstoneMessageEntry.isDeleted = true 50 51 // Set the mappings to point to the tombstone entry. 52 bav._setMessageEntryMappings(&tombstoneMessageEntry) 53 } 54 55 // 56 // Postgres messages 57 // 58 59 func (bav *UtxoView) getMessage(messageHash *BlockHash) *PGMessage { 60 mapValue, existsMapValue := bav.MessageMap[*messageHash] 61 if existsMapValue { 62 return mapValue 63 } 64 65 message := bav.Postgres.GetMessage(messageHash) 66 if message != nil { 67 bav.setMessageMappings(message) 68 } 69 return message 70 } 71 72 func (bav *UtxoView) setMessageMappings(message *PGMessage) { 73 bav.MessageMap[*message.MessageHash] = message 74 } 75 76 func (bav *UtxoView) deleteMessageMappings(message *PGMessage) { 77 deletedMessage := *message 78 deletedMessage.isDeleted = true 79 bav.setMessageMappings(&deletedMessage) 80 } 81 82 // TODO: Update for Postgres 83 func (bav *UtxoView) GetMessagesForUser(publicKey []byte) ( 84 _messageEntries []*MessageEntry, _err error) { 85 86 // Start by fetching all the messages we have in the db. 87 dbMessageEntries, err := DbGetMessageEntriesForPublicKey(bav.Handle, publicKey) 88 if err != nil { 89 return nil, errors.Wrapf(err, "GetMessagesForUser: Problem fetching MessageEntrys from db: ") 90 } 91 92 // Iterate through the entries found in the db and force the view to load them. 93 // This fills in any gaps in the view so that, after this, the view should contain 94 // the union of what it had before plus what was in the db. 95 for _, dbMessageEntry := range dbMessageEntries { 96 messageKey := MakeMessageKey(publicKey, dbMessageEntry.TstampNanos) 97 bav._getMessageEntryForMessageKey(&messageKey) 98 } 99 100 // Now that the view mappings are a complete picture, iterate through them 101 // and set them on the map we're returning. Skip entries that don't match 102 // our public key or that are deleted. Note that only considering mappings 103 // where our public key is part of the key should ensure there are no 104 // duplicates in the resulting list. 105 messageEntriesToReturn := []*MessageEntry{} 106 for viewMessageKey, viewMessageEntry := range bav.MessageKeyToMessageEntry { 107 if viewMessageEntry.isDeleted { 108 continue 109 } 110 messageKey := MakeMessageKey(publicKey, viewMessageEntry.TstampNanos) 111 if viewMessageKey != messageKey { 112 continue 113 } 114 115 // At this point we are confident the map key is equal to the message 116 // key containing the passed-in public key so add it to the mapping. 117 messageEntriesToReturn = append(messageEntriesToReturn, viewMessageEntry) 118 } 119 120 return messageEntriesToReturn, nil 121 } 122 123 // TODO: Update for Postgres 124 func (bav *UtxoView) GetLimitedMessagesForUser(publicKey []byte) ( 125 _messageEntries []*MessageEntry, _err error) { 126 127 // Start by fetching all the messages we have in the db. 128 dbMessageEntries, err := DbGetLimitedMessageEntriesForPublicKey(bav.Handle, publicKey) 129 if err != nil { 130 return nil, errors.Wrapf(err, "GetMessagesForUser: Problem fetching MessageEntrys from db: ") 131 } 132 133 // Iterate through the entries found in the db and force the view to load them. 134 // This fills in any gaps in the view so that, after this, the view should contain 135 // the union of what it had before plus what was in the db. 136 for _, dbMessageEntry := range dbMessageEntries { 137 messageKey := MakeMessageKey(publicKey, dbMessageEntry.TstampNanos) 138 bav._getMessageEntryForMessageKey(&messageKey) 139 } 140 141 // Now that the view mappings are a complete picture, iterate through them 142 // and set them on the map we're returning. Skip entries that don't match 143 // our public key or that are deleted. Note that only considering mappings 144 // where our public key is part of the key should ensure there are no 145 // duplicates in the resulting list. 146 messageEntriesToReturn := []*MessageEntry{} 147 for viewMessageKey, viewMessageEntry := range bav.MessageKeyToMessageEntry { 148 if viewMessageEntry.isDeleted { 149 continue 150 } 151 messageKey := MakeMessageKey(publicKey, viewMessageEntry.TstampNanos) 152 if viewMessageKey != messageKey { 153 continue 154 } 155 156 // At this point we are confident the map key is equal to the message 157 // key containing the passed-in public key so add it to the mapping. 158 messageEntriesToReturn = append(messageEntriesToReturn, viewMessageEntry) 159 } 160 161 return messageEntriesToReturn, nil 162 } 163 164 func (bav *UtxoView) _connectPrivateMessage( 165 txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) ( 166 _totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) { 167 168 // Check that the transaction has the right TxnType. 169 if txn.TxnMeta.GetTxnType() != TxnTypePrivateMessage { 170 return 0, 0, nil, fmt.Errorf("_connectPrivateMessage: called with bad TxnType %s", 171 txn.TxnMeta.GetTxnType().String()) 172 } 173 txMeta := txn.TxnMeta.(*PrivateMessageMetadata) 174 175 // Check the length of the EncryptedText 176 if uint64(len(txMeta.EncryptedText)) > bav.Params.MaxPrivateMessageLengthBytes { 177 return 0, 0, nil, errors.Wrapf( 178 RuleErrorPrivateMessageEncryptedTextLengthExceedsMax, "_connectPrivateMessage: "+ 179 "EncryptedTextLen = %d; Max length = %d", 180 len(txMeta.EncryptedText), bav.Params.MaxPrivateMessageLengthBytes) 181 } 182 183 // Check that a proper public key is provided in the message metadata 184 if len(txMeta.RecipientPublicKey) != btcec.PubKeyBytesLenCompressed { 185 return 0, 0, nil, errors.Wrapf( 186 RuleErrorPrivateMessageRecipientPubKeyLen, "_connectPrivateMessage: "+ 187 "RecipientPubKeyLen = %d; Expected length = %d", 188 len(txMeta.RecipientPublicKey), btcec.PubKeyBytesLenCompressed) 189 } 190 _, err := btcec.ParsePubKey(txMeta.RecipientPublicKey, btcec.S256()) 191 if err != nil { 192 return 0, 0, nil, errors.Wrapf( 193 RuleErrorPrivateMessageParsePubKeyError, "_connectPrivateMessage: Parse error: %v", err) 194 } 195 196 // You can't send a message to yourself. 197 if reflect.DeepEqual(txn.PublicKey, txMeta.RecipientPublicKey) { 198 return 0, 0, nil, errors.Wrapf( 199 RuleErrorPrivateMessageSenderPublicKeyEqualsRecipientPublicKey, 200 "_connectPrivateMessage: Parse error: %v", err) 201 } 202 203 // Check that the timestamp is greater than zero. Not doing this could make 204 // the message not get returned when we call Seek() in our db. It's also just 205 // a reasonable sanity check. 206 if txMeta.TimestampNanos == 0 { 207 return 0, 0, nil, RuleErrorPrivateMessageTstampIsZero 208 } 209 210 // Connect basic txn to get the total input and the total output without 211 // considering the transaction metadata. 212 totalInput, totalOutput, utxoOpsForTxn, err := bav._connectBasicTransfer( 213 txn, txHash, blockHeight, verifySignatures) 214 if err != nil { 215 return 0, 0, nil, errors.Wrapf(err, "_connectPrivateMessage: ") 216 } 217 218 // At this point the inputs and outputs have been processed. Now we 219 // need to handle the metadata. 220 221 // If a message already exists and does not have isDeleted=true then return 222 // an error. In general, messages must have unique (pubkey, tstamp) tuples. 223 // 224 // Postgres does not enforce these rule errors 225 if bav.Postgres == nil { 226 senderMessageKey := MakeMessageKey(txn.PublicKey, txMeta.TimestampNanos) 227 senderMessage := bav._getMessageEntryForMessageKey(&senderMessageKey) 228 if senderMessage != nil && !senderMessage.isDeleted { 229 return 0, 0, nil, errors.Wrapf( 230 RuleErrorPrivateMessageExistsWithSenderPublicKeyTstampTuple, 231 "_connectPrivateMessage: Message key: %v", &senderMessageKey) 232 } 233 recipientMessageKey := MakeMessageKey(txMeta.RecipientPublicKey, txMeta.TimestampNanos) 234 recipientMessage := bav._getMessageEntryForMessageKey(&recipientMessageKey) 235 if recipientMessage != nil && !recipientMessage.isDeleted { 236 return 0, 0, nil, errors.Wrapf( 237 RuleErrorPrivateMessageExistsWithRecipientPublicKeyTstampTuple, 238 "_connectPrivateMessage: Message key: %v", &recipientMessageKey) 239 } 240 } 241 242 if verifySignatures { 243 // _connectBasicTransfer has already checked that the transaction is 244 // signed by the top-level public key, which we take to be the sender's 245 // public key so there is no need to verify anything further. 246 } 247 248 // At this point we are confident that we are parsing a message with a unique 249 // <PublicKey, TstampNanos> tuple. We also know that the sender and recipient 250 // have different public keys. 251 252 // Create a MessageEntry 253 messageEntry := &MessageEntry{ 254 SenderPublicKey: txn.PublicKey, 255 RecipientPublicKey: txMeta.RecipientPublicKey, 256 EncryptedText: txMeta.EncryptedText, 257 TstampNanos: txMeta.TimestampNanos, 258 Version: 1, 259 } 260 261 //Check if message is encrypted with shared secret 262 extraV, hasExtraV := txn.ExtraData["V"] 263 if hasExtraV { 264 Version, _ := Uvarint(extraV) 265 messageEntry.Version = uint8(Version) 266 } 267 268 if bav.Postgres != nil { 269 message := &PGMessage{ 270 MessageHash: txn.Hash(), 271 SenderPublicKey: txn.PublicKey, 272 RecipientPublicKey: txMeta.RecipientPublicKey, 273 EncryptedText: txMeta.EncryptedText, 274 TimestampNanos: txMeta.TimestampNanos, 275 } 276 277 bav.setMessageMappings(message) 278 } else { 279 // Set the mappings in our in-memory map for the MessageEntry. 280 bav._setMessageEntryMappings(messageEntry) 281 } 282 283 // Add an operation to the list at the end indicating we've added a message 284 // to our data structure. 285 utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{ 286 Type: OperationTypePrivateMessage, 287 }) 288 289 return totalInput, totalOutput, utxoOpsForTxn, nil 290 } 291 292 // TODO: Update for postgres 293 func (bav *UtxoView) _disconnectPrivateMessage( 294 operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash, 295 utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error { 296 297 // Verify that the last operation is a PrivateMessage opration 298 if len(utxoOpsForTxn) == 0 { 299 return fmt.Errorf("_disconnectPrivateMessage: utxoOperations are missing") 300 } 301 operationIndex := len(utxoOpsForTxn) - 1 302 if utxoOpsForTxn[operationIndex].Type != OperationTypePrivateMessage { 303 return fmt.Errorf("_disconnectPrivateMessage: Trying to revert "+ 304 "OperationTypePrivateMessage but found type %v", 305 utxoOpsForTxn[operationIndex].Type) 306 } 307 308 // Now we know the txMeta is PrivateMessage 309 txMeta := currentTxn.TxnMeta.(*PrivateMessageMetadata) 310 311 // Get the MessageEntry for the sender in the transaction. If we don't find 312 // it or if it has isDeleted=true that's an error. 313 senderMessageKey := MakeMessageKey(currentTxn.PublicKey, txMeta.TimestampNanos) 314 messageEntry := bav._getMessageEntryForMessageKey(&senderMessageKey) 315 if messageEntry == nil || messageEntry.isDeleted { 316 return fmt.Errorf("_disconnectPrivateMessage: MessageEntry for "+ 317 "SenderMessageKey %v was found to be nil or deleted: %v", 318 &senderMessageKey, messageEntry) 319 } 320 321 // Verify that the sender and recipient in the entry match the TxnMeta as 322 // a sanity check. 323 if !reflect.DeepEqual(messageEntry.SenderPublicKey, currentTxn.PublicKey) { 324 return fmt.Errorf("_disconnectPrivateMessage: Sender public key on "+ 325 "MessageEntry was %s but the PublicKey on the txn was %s", 326 PkToString(messageEntry.SenderPublicKey, bav.Params), 327 PkToString(currentTxn.PublicKey, bav.Params)) 328 } 329 if !reflect.DeepEqual(messageEntry.RecipientPublicKey, txMeta.RecipientPublicKey) { 330 return fmt.Errorf("_disconnectPrivateMessage: Recipient public key on "+ 331 "MessageEntry was %s but the PublicKey on the TxnMeta was %s", 332 PkToString(messageEntry.RecipientPublicKey, bav.Params), 333 PkToString(txMeta.RecipientPublicKey, bav.Params)) 334 } 335 // Sanity-check that the MessageEntry TstampNanos matches the transaction. 336 if messageEntry.TstampNanos != txMeta.TimestampNanos { 337 return fmt.Errorf("_disconnectPrivateMessage: TimestampNanos in "+ 338 "MessageEntry was %d but in transaction it was %d", 339 messageEntry.TstampNanos, 340 txMeta.TimestampNanos) 341 } 342 // Sanity-check that the EncryptedText on the MessageEntry matches the transaction 343 // just for good measure. 344 if !reflect.DeepEqual(messageEntry.EncryptedText, txMeta.EncryptedText) { 345 return fmt.Errorf("_disconnectPrivateMessage: EncryptedText in MessageEntry "+ 346 "did not match EncryptedText in transaction: (%s) != (%s)", 347 hex.EncodeToString(messageEntry.EncryptedText), 348 hex.EncodeToString(txMeta.EncryptedText)) 349 } 350 351 // Now that we are confident the MessageEntry lines up with the transaction we're 352 // rolling back, use the entry to delete the mappings for this message. 353 bav._deleteMessageEntryMappings(messageEntry) 354 355 // Now revert the basic transfer with the remaining operations. Cut off 356 // the PrivateMessage operation at the end since we just reverted it. 357 return bav._disconnectBasicTransfer( 358 currentTxn, txnHash, utxoOpsForTxn[:operationIndex], blockHeight) 359 }