github.com/deso-protocol/core@v1.2.9/lib/block_view_profile.go (about) 1 package lib 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "github.com/btcsuite/btcd/btcec" 7 "github.com/davecgh/go-spew/spew" 8 "github.com/golang/glog" 9 "github.com/pkg/errors" 10 "reflect" 11 "sort" 12 "strings" 13 ) 14 15 // Just fetch all the profiles from the db and join them with all the profiles 16 // in the mempool. Then sort them by their DeSo. This can be called 17 // on an empty view or a view that already has a lot of transactions 18 // applied to it. 19 func (bav *UtxoView) GetAllProfiles(readerPK []byte) ( 20 _profiles map[PkMapKey]*ProfileEntry, 21 _corePostsByProfilePublicKey map[PkMapKey][]*PostEntry, 22 _commentsByProfilePublicKey map[PkMapKey][]*PostEntry, 23 _postEntryReaderStates map[BlockHash]*PostEntryReaderState, _err error) { 24 // Start by fetching all the profiles we have in the db. 25 // 26 // TODO(performance): This currently fetches all profiles. We should implement 27 // some kind of pagination instead though. 28 _, _, dbProfileEntries, err := DBGetAllProfilesByCoinValue(bav.Handle, true /*fetchEntries*/) 29 if err != nil { 30 return nil, nil, nil, nil, errors.Wrapf( 31 err, "GetAllProfiles: Problem fetching ProfileEntrys from db: ") 32 } 33 34 // Iterate through the entries found in the db and force the view to load them. 35 // This fills in any gaps in the view so that, after this, the view should contain 36 // the union of what it had before plus what was in the db. 37 for _, dbProfileEntry := range dbProfileEntries { 38 bav.GetProfileEntryForPublicKey(dbProfileEntry.PublicKey) 39 } 40 41 // At this point, all the profiles should be loaded into the view. 42 43 // Do one more pass to load all the comments associated with each 44 // profile into the view. 45 commentsByProfilePublicKey := make(map[PkMapKey][]*PostEntry) 46 for _, profileEntry := range bav.ProfilePKIDToProfileEntry { 47 // Ignore deleted or rolled-back posts. 48 if profileEntry.isDeleted { 49 continue 50 } 51 commentsByProfilePublicKey[MakePkMapKey(profileEntry.PublicKey)] = []*PostEntry{} 52 _, dbCommentHashes, _, err := DBGetCommentPostHashesForParentStakeID( 53 bav.Handle, profileEntry.PublicKey, false /*fetchEntries*/) 54 if err != nil { 55 return nil, nil, nil, nil, errors.Wrapf(err, "GetAllPosts: Problem fetching comment PostEntry's from db: ") 56 } 57 for _, commentHash := range dbCommentHashes { 58 bav.GetPostEntryForPostHash(commentHash) 59 } 60 } 61 // TODO(performance): Because we want to load all the posts the profile 62 // has made, just go ahead and load *all* the posts into the view so that 63 // they'll get returned in the mapping. Later, we should use the db index 64 // to do this. 65 _, _, dbPostEntries, err := DBGetAllPostsByTstamp(bav.Handle, true /*fetchEntries*/) 66 if err != nil { 67 return nil, nil, nil, nil, errors.Wrapf( 68 err, "GetAllPosts: Problem fetching PostEntry's from db: ") 69 } 70 for _, dbPostEntry := range dbPostEntries { 71 bav.GetPostEntryForPostHash(dbPostEntry.PostHash) 72 } 73 74 // Iterate through all the posts loaded into the view and attach them 75 // to the relevant profiles. Also adds reader state if a reader pubkey is provided. 76 corePostsByPublicKey := make(map[PkMapKey][]*PostEntry) 77 postEntryReaderStates := make(map[BlockHash]*PostEntryReaderState) 78 for _, postEntry := range bav.PostHashToPostEntry { 79 // Ignore deleted or rolled-back posts. 80 if postEntry.isDeleted { 81 continue 82 } 83 84 // If the post has a stakeID that corresponds to a profile then add 85 // it to our map. 86 // Every post is either a core post or a comment. If it has a stake ID 87 // its a comment, and if it doesn't then it's a core post. 88 if len(postEntry.ParentStakeID) == 0 { 89 // In this case we are dealing with a "core" post so add it to the 90 // core post map. 91 corePostsForProfile := corePostsByPublicKey[MakePkMapKey(postEntry.PosterPublicKey)] 92 corePostsForProfile = append(corePostsForProfile, postEntry) 93 corePostsByPublicKey[MakePkMapKey(postEntry.PosterPublicKey)] = corePostsForProfile 94 } else { 95 // Add the comment to our map. 96 commentsForProfile := commentsByProfilePublicKey[MakePkMapKey(postEntry.ParentStakeID)] 97 commentsForProfile = append(commentsForProfile, postEntry) 98 commentsByProfilePublicKey[MakePkMapKey(postEntry.ParentStakeID)] = commentsForProfile 99 } 100 101 // Create reader state map. Ie, whether the reader has liked the post, etc. 102 // If nil is passed in as the readerPK, this is skipped. 103 if readerPK != nil { 104 postEntryReaderState := bav.GetPostEntryReaderState(readerPK, postEntry) 105 postEntryReaderStates[*postEntry.PostHash] = postEntryReaderState 106 } 107 } 108 109 // Now that the view mappings are a complete picture, iterate through them 110 // and set them on the map we're returning. 111 profilesByPublicKey := make(map[PkMapKey]*ProfileEntry) 112 for _, profileEntry := range bav.ProfilePKIDToProfileEntry { 113 // Ignore deleted or rolled-back posts. 114 if profileEntry.isDeleted { 115 continue 116 } 117 profilesByPublicKey[MakePkMapKey(profileEntry.PublicKey)] = profileEntry 118 } 119 120 // Sort all the comment lists. Here we put the latest comment at the 121 // end. 122 for _, commentList := range commentsByProfilePublicKey { 123 sort.Slice(commentList, func(ii, jj int) bool { 124 return commentList[ii].TimestampNanos < commentList[jj].TimestampNanos 125 }) 126 } 127 128 return profilesByPublicKey, corePostsByPublicKey, commentsByProfilePublicKey, postEntryReaderStates, nil 129 } 130 131 func (bav *UtxoView) GetProfileEntryForUsername(nonLowercaseUsername []byte) *ProfileEntry { 132 // If an entry exists in the in-memory map, return the value of that mapping. 133 134 // Note that the call to MakeUsernameMapKey will lowercase the username 135 // and thus enforce a uniqueness check. 136 mapKey := MakeUsernameMapKey(nonLowercaseUsername) 137 mapValue, existsMapValue := bav.ProfileUsernameToProfileEntry[mapKey] 138 if existsMapValue { 139 return mapValue 140 } 141 142 // If we get here it means no value exists in our in-memory map. In this case, 143 // defer to the db. If a mapping exists in the db, return it. If not, return 144 // nil. 145 // Note that the DB username lookup is case-insensitive. 146 if bav.Postgres != nil { 147 profile := bav.Postgres.GetProfileForUsername(string(nonLowercaseUsername)) 148 if profile == nil { 149 bav.ProfileUsernameToProfileEntry[mapKey] = nil 150 return nil 151 } 152 153 profileEntry, _ := bav.setProfileMappings(profile) 154 return profileEntry 155 } else { 156 dbProfileEntry := DBGetProfileEntryForUsername(bav.Handle, nonLowercaseUsername) 157 if dbProfileEntry != nil { 158 bav._setProfileEntryMappings(dbProfileEntry) 159 } 160 return dbProfileEntry 161 } 162 } 163 164 func (bav *UtxoView) GetPKIDForPublicKey(publicKey []byte) *PKIDEntry { 165 // If an entry exists in the in-memory map, return the value of that mapping. 166 mapValue, existsMapValue := bav.PublicKeyToPKIDEntry[MakePkMapKey(publicKey)] 167 if existsMapValue { 168 return mapValue 169 } 170 171 // If we get here it means no value exists in our in-memory map. In this case, 172 // defer to the db. If a mapping exists in the db, return it. If not, return 173 // nil. 174 // 175 // Note that we construct an entry from the DB return value in order to track 176 // isDeleted on the view. If not for isDeleted, we wouldn't need the PKIDEntry 177 // wrapper. 178 if bav.Postgres != nil { 179 profile := bav.Postgres.GetProfileForPublicKey(publicKey) 180 if profile == nil { 181 pkidEntry := &PKIDEntry{ 182 PKID: PublicKeyToPKID(publicKey), 183 PublicKey: publicKey, 184 } 185 bav._setPKIDMappings(pkidEntry) 186 return pkidEntry 187 } 188 189 _, pkidEntry := bav.setProfileMappings(profile) 190 return pkidEntry 191 } else { 192 dbPKIDEntry := DBGetPKIDEntryForPublicKey(bav.Handle, publicKey) 193 if dbPKIDEntry != nil { 194 bav._setPKIDMappings(dbPKIDEntry) 195 } 196 return dbPKIDEntry 197 } 198 } 199 200 func (bav *UtxoView) GetPublicKeyForPKID(pkid *PKID) []byte { 201 // If an entry exists in the in-memory map, return the value of that mapping. 202 mapValue, existsMapValue := bav.PKIDToPublicKey[*pkid] 203 if existsMapValue { 204 return mapValue.PublicKey 205 } 206 207 // If we get here it means no value exists in our in-memory map. In this case, 208 // defer to the db. If a mapping exists in the db, return it. If not, return 209 // nil. 210 // 211 // Note that we construct an entry from the DB return value in order to track 212 // isDeleted on the view. If not for isDeleted, we wouldn't need the PKIDEntry 213 // wrapper. 214 if bav.Postgres != nil { 215 profile := bav.Postgres.GetProfile(*pkid) 216 if profile == nil { 217 pkidEntry := &PKIDEntry{ 218 PKID: pkid, 219 PublicKey: PKIDToPublicKey(pkid), 220 } 221 bav._setPKIDMappings(pkidEntry) 222 return pkidEntry.PublicKey 223 } 224 225 _, pkidEntry := bav.setProfileMappings(profile) 226 return pkidEntry.PublicKey 227 } else { 228 dbPublicKey := DBGetPublicKeyForPKID(bav.Handle, pkid) 229 if len(dbPublicKey) != 0 { 230 bav._setPKIDMappings(&PKIDEntry{ 231 PKID: pkid, 232 PublicKey: dbPublicKey, 233 }) 234 } 235 return dbPublicKey 236 } 237 } 238 239 func (bav *UtxoView) _setPKIDMappings(pkidEntry *PKIDEntry) { 240 // This function shouldn't be called with nil. 241 if pkidEntry == nil { 242 glog.Errorf("_setPKIDMappings: Called with nil PKID; " + 243 "this should never happen.") 244 return 245 } 246 247 // Add a mapping for the profile and add the reverse mapping as well. 248 bav.PublicKeyToPKIDEntry[MakePkMapKey(pkidEntry.PublicKey)] = pkidEntry 249 bav.PKIDToPublicKey[*(pkidEntry.PKID)] = pkidEntry 250 } 251 252 func (bav *UtxoView) _deletePKIDMappings(pkid *PKIDEntry) { 253 // Create a tombstone entry. 254 tombstonePKIDEntry := *pkid 255 tombstonePKIDEntry.isDeleted = true 256 257 // Set the mappings to point to the tombstone entry. 258 bav._setPKIDMappings(&tombstonePKIDEntry) 259 } 260 261 func (bav *UtxoView) GetProfileEntryForPublicKey(publicKey []byte) *ProfileEntry { 262 // Get the PKID for the public key provided. This should never return nil if a 263 // proper public key is provided. 264 pkidEntry := bav.GetPKIDForPublicKey(publicKey) 265 if pkidEntry == nil || pkidEntry.isDeleted { 266 return nil 267 } 268 269 return bav.GetProfileEntryForPKID(pkidEntry.PKID) 270 } 271 272 func (bav *UtxoView) GetProfileEntryForPKID(pkid *PKID) *ProfileEntry { 273 // If an entry exists in the in-memory map, return the value of that mapping. 274 mapValue, existsMapValue := bav.ProfilePKIDToProfileEntry[*pkid] 275 if existsMapValue { 276 return mapValue 277 } 278 279 // If we get here it means no value exists in our in-memory map. In this case, 280 // defer to the db. If a mapping exists in the db, return it. If not, return 281 // nil. 282 if bav.Postgres != nil { 283 // Note: We should never get here but writing this code just in case 284 profile := bav.Postgres.GetProfile(*pkid) 285 if profile == nil { 286 return nil 287 } 288 289 profileEntry, _ := bav.setProfileMappings(profile) 290 return profileEntry 291 } else { 292 dbProfileEntry := DBGetProfileEntryForPKID(bav.Handle, pkid) 293 if dbProfileEntry != nil { 294 bav._setProfileEntryMappings(dbProfileEntry) 295 } 296 return dbProfileEntry 297 } 298 } 299 300 func (bav *UtxoView) _setProfileEntryMappings(profileEntry *ProfileEntry) { 301 // This function shouldn't be called with nil. 302 if profileEntry == nil { 303 glog.Errorf("_setProfileEntryMappings: Called with nil ProfileEntry; " + 304 "this should never happen.") 305 return 306 } 307 308 // Look up the current PKID for the profile. Never nil because we create the entry if it doesn't exist 309 pkidEntry := bav.GetPKIDForPublicKey(profileEntry.PublicKey) 310 311 // Add a mapping for the profile. 312 bav.ProfilePKIDToProfileEntry[*pkidEntry.PKID] = profileEntry 313 // Note the username will be lowercased when used as a map key. 314 bav.ProfileUsernameToProfileEntry[MakeUsernameMapKey(profileEntry.Username)] = profileEntry 315 } 316 317 func (bav *UtxoView) _deleteProfileEntryMappings(profileEntry *ProfileEntry) { 318 // Create a tombstone entry. 319 tombstoneProfileEntry := *profileEntry 320 tombstoneProfileEntry.isDeleted = true 321 322 // Set the mappings to point to the tombstone entry. 323 bav._setProfileEntryMappings(&tombstoneProfileEntry) 324 } 325 326 // _getDerivedKeyMappingForOwner fetches the derived key mapping from the utxoView 327 func (bav *UtxoView) _getDerivedKeyMappingForOwner(ownerPublicKey []byte, derivedPublicKey []byte) *DerivedKeyEntry { 328 // Check if the entry exists in utxoView. 329 ownerPk := NewPublicKey(ownerPublicKey) 330 derivedPk := NewPublicKey(derivedPublicKey) 331 derivedKeyMapKey := MakeDerivedKeyMapKey(*ownerPk, *derivedPk) 332 entry, exists := bav.DerivedKeyToDerivedEntry[derivedKeyMapKey] 333 if exists { 334 return entry 335 } 336 337 // Check if the entry exists in the DB. 338 if bav.Postgres != nil { 339 if entryPG := bav.Postgres.GetDerivedKey(ownerPk, derivedPk); entryPG != nil { 340 entry = entryPG.NewDerivedKeyEntry() 341 } else { 342 entry = nil 343 } 344 } else { 345 entry = DBGetOwnerToDerivedKeyMapping(bav.Handle, *ownerPk, *derivedPk) 346 } 347 348 // If an entry exists, update the UtxoView map. 349 if entry != nil { 350 bav._setDerivedKeyMapping(entry) 351 return entry 352 } 353 return nil 354 } 355 356 // GetAllDerivedKeyMappingsForOwner fetches all derived key mappings belonging to an owner. 357 func (bav *UtxoView) GetAllDerivedKeyMappingsForOwner(ownerPublicKey []byte) ( 358 map[PublicKey]*DerivedKeyEntry, error) { 359 derivedKeyMappings := make(map[PublicKey]*DerivedKeyEntry) 360 361 // Check for entries in UtxoView. 362 for entryKey, entry := range bav.DerivedKeyToDerivedEntry { 363 if reflect.DeepEqual(entryKey.OwnerPublicKey[:], ownerPublicKey) { 364 derivedKeyMappings[entryKey.DerivedPublicKey] = entry 365 } 366 } 367 368 // Check for entries in DB. 369 var dbMappings []*DerivedKeyEntry 370 ownerPk := NewPublicKey(ownerPublicKey) 371 if bav.Postgres != nil { 372 pgMappings := bav.Postgres.GetAllDerivedKeysForOwner(ownerPk) 373 for _, entry := range pgMappings { 374 dbMappings = append(dbMappings, entry.NewDerivedKeyEntry()) 375 } 376 } else { 377 var err error 378 dbMappings, err = DBGetAllOwnerToDerivedKeyMappings(bav.Handle, *ownerPk) 379 if err != nil { 380 return nil, errors.Wrapf(err, "GetAllDerivedKeyMappingsForOwner: problem looking up"+ 381 "entries in the DB.") 382 } 383 } 384 385 // Add entries from the DB that aren't already present. 386 for _, entry := range dbMappings { 387 mapKey := entry.DerivedPublicKey 388 if _, ok := derivedKeyMappings[mapKey]; !ok { 389 derivedKeyMappings[mapKey] = entry 390 } 391 } 392 393 // Delete entries with isDeleted=true. We are deleting these entries 394 // only now, because we wanted to skip corresponding keys in DB fetch. 395 for entryKey, entry := range derivedKeyMappings { 396 if entry.isDeleted { 397 delete(derivedKeyMappings, entryKey) 398 } 399 } 400 401 return derivedKeyMappings, nil 402 } 403 404 // _setDerivedKeyMapping sets a derived key mapping in the utxoView. 405 func (bav *UtxoView) _setDerivedKeyMapping(derivedKeyEntry *DerivedKeyEntry) { 406 // If the derivedKeyEntry is nil then there's nothing to do. 407 if derivedKeyEntry == nil { 408 return 409 } 410 // Add a mapping for the derived key. 411 derivedKeyMapKey := MakeDerivedKeyMapKey(derivedKeyEntry.OwnerPublicKey, derivedKeyEntry.DerivedPublicKey) 412 bav.DerivedKeyToDerivedEntry[derivedKeyMapKey] = derivedKeyEntry 413 } 414 415 // _deleteDerivedKeyMapping deletes a derived key mapping from utxoView. 416 func (bav *UtxoView) _deleteDerivedKeyMapping(derivedKeyEntry *DerivedKeyEntry) { 417 // If the derivedKeyEntry is nil then there's nothing to do. 418 if derivedKeyEntry == nil { 419 return 420 } 421 422 // Create a tombstone entry. 423 tombstoneDerivedKeyEntry := *derivedKeyEntry 424 tombstoneDerivedKeyEntry.isDeleted = true 425 426 // Set the mappings to point to the tombstone entry. 427 bav._setDerivedKeyMapping(&tombstoneDerivedKeyEntry) 428 } 429 430 // Takes a Postgres Profile, sets all the mappings on the view, returns the equivalent ProfileEntry and PKIDEntry 431 func (bav *UtxoView) setProfileMappings(profile *PGProfile) (*ProfileEntry, *PKIDEntry) { 432 pkidEntry := &PKIDEntry{ 433 PKID: profile.PKID, 434 PublicKey: profile.PublicKey.ToBytes(), 435 } 436 bav._setPKIDMappings(pkidEntry) 437 438 var profileEntry *ProfileEntry 439 440 // Postgres stores profiles with empty usernames when a swap identity occurs. 441 // Storing a nil value for the profile entry preserves badger behavior 442 if profile.Empty() { 443 bav.ProfilePKIDToProfileEntry[*pkidEntry.PKID] = nil 444 } else { 445 profileEntry = &ProfileEntry{ 446 PublicKey: profile.PublicKey.ToBytes(), 447 Username: []byte(profile.Username), 448 Description: []byte(profile.Description), 449 ProfilePic: profile.ProfilePic, 450 CoinEntry: CoinEntry{ 451 CreatorBasisPoints: profile.CreatorBasisPoints, 452 DeSoLockedNanos: profile.DeSoLockedNanos, 453 NumberOfHolders: profile.NumberOfHolders, 454 CoinsInCirculationNanos: profile.CoinsInCirculationNanos, 455 CoinWatermarkNanos: profile.CoinWatermarkNanos, 456 }, 457 } 458 459 bav._setProfileEntryMappings(profileEntry) 460 } 461 462 return profileEntry, pkidEntry 463 } 464 465 func (bav *UtxoView) GetProfilesByCoinValue(startLockedNanos uint64, limit int) []*ProfileEntry { 466 profiles := bav.Postgres.GetProfilesByCoinValue(startLockedNanos, limit) 467 var profileEntrys []*ProfileEntry 468 for _, profile := range profiles { 469 profileEntry, _ := bav.setProfileMappings(profile) 470 profileEntrys = append(profileEntrys, profileEntry) 471 } 472 return profileEntrys 473 } 474 475 func (bav *UtxoView) GetProfilesForUsernamePrefixByCoinValue(usernamePrefix string) []*ProfileEntry { 476 profiles := bav.Postgres.GetProfilesForUsernamePrefixByCoinValue(usernamePrefix, 50) 477 pubKeysMap := make(map[PkMapKey][]byte) 478 479 // TODO: We are overwriting profiles here which is awful 480 for _, profile := range profiles { 481 bav.setProfileMappings(profile) 482 } 483 484 lowercaseUsernamePrefixString := strings.ToLower(usernamePrefix) 485 var profileEntrys []*ProfileEntry 486 for _, pk := range pubKeysMap { 487 pkid := bav.GetPKIDForPublicKey(pk).PKID 488 profile := bav.GetProfileEntryForPKID(pkid) 489 // Double-check that a username matches the prefix. 490 // If a user had the handle "elon" and then changed to "jeff" and that transaction hadn't mined yet, 491 // we would return the profile for "jeff" when we search for "elon" which is incorrect. 492 if profile != nil && strings.HasPrefix(strings.ToLower(string(profile.Username[:])), lowercaseUsernamePrefixString) { 493 profileEntrys = append(profileEntrys, profile) 494 } 495 } 496 497 // Username searches are always sorted by coin value. 498 sort.Slice(profileEntrys, func(ii, jj int) bool { 499 return profileEntrys[ii].CoinEntry.DeSoLockedNanos > profileEntrys[jj].CoinEntry.DeSoLockedNanos 500 }) 501 502 return profileEntrys 503 } 504 505 func (bav *UtxoView) _connectUpdateProfile( 506 txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool, 507 ignoreUtxos bool) ( 508 _totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) { 509 510 // Check that the transaction has the right TxnType. 511 if txn.TxnMeta.GetTxnType() != TxnTypeUpdateProfile { 512 return 0, 0, nil, fmt.Errorf("_connectUpdateProfile: called with bad TxnType %s", 513 txn.TxnMeta.GetTxnType().String()) 514 } 515 txMeta := txn.TxnMeta.(*UpdateProfileMetadata) 516 517 // See comment on ForgivenProfileUsernameClaims. This fixes a bug in the blockchain 518 // where users could claim usernames that weren't actually available. 519 if forgivenUsername, exists := ForgivenProfileUsernameClaims[*txHash]; exists { 520 // Make a copy of txMeta and assign it to the existing txMeta so we avoid 521 // modifying the fields. 522 newTxMeta := *txMeta 523 newTxMeta.NewUsername = []byte(forgivenUsername) 524 txMeta = &newTxMeta 525 } 526 527 // Validate the fields to make sure they don't exceed our limits. 528 if uint64(len(txMeta.NewUsername)) > bav.Params.MaxUsernameLengthBytes { 529 return 0, 0, nil, RuleErrorProfileUsernameTooLong 530 } 531 if uint64(len(txMeta.NewDescription)) > bav.Params.MaxUserDescriptionLengthBytes { 532 return 0, 0, nil, RuleErrorProfileDescriptionTooLong 533 } 534 if uint64(len(txMeta.NewProfilePic)) > bav.Params.MaxProfilePicLengthBytes { 535 return 0, 0, nil, RuleErrorMaxProfilePicSize 536 } 537 if txMeta.NewCreatorBasisPoints > bav.Params.MaxCreatorBasisPoints || txMeta.NewCreatorBasisPoints < 0 { 538 return 0, 0, nil, RuleErrorProfileCreatorPercentageSize 539 } 540 if txMeta.NewStakeMultipleBasisPoints <= 100*100 || 541 txMeta.NewStakeMultipleBasisPoints > bav.Params.MaxStakeMultipleBasisPoints { 542 543 return 0, 0, nil, RuleErrorProfileStakeMultipleSize 544 } 545 // If a username is set then it must adhere to a particular regex. 546 if len(txMeta.NewUsername) != 0 && !UsernameRegex.Match(txMeta.NewUsername) { 547 return 0, 0, nil, errors.Wrapf(RuleErrorInvalidUsername, "Username: %v", string(txMeta.NewUsername)) 548 } 549 550 profilePublicKey := txn.PublicKey 551 _, updaterIsParamUpdater := bav.Params.ParamUpdaterPublicKeys[MakePkMapKey(txn.PublicKey)] 552 if len(txMeta.ProfilePublicKey) != 0 { 553 if len(txMeta.ProfilePublicKey) != btcec.PubKeyBytesLenCompressed { 554 return 0, 0, nil, errors.Wrapf(RuleErrorProfilePublicKeySize, "_connectUpdateProfile: %#v", txMeta.ProfilePublicKey) 555 } 556 _, err := btcec.ParsePubKey(txMeta.ProfilePublicKey, btcec.S256()) 557 if err != nil { 558 return 0, 0, nil, errors.Wrapf(RuleErrorProfileBadPublicKey, "_connectUpdateProfile: %v", err) 559 } 560 profilePublicKey = txMeta.ProfilePublicKey 561 562 if blockHeight > UpdateProfileFixBlockHeight { 563 // Make sure that either (1) the profile pub key is the txn signer's public key or 564 // (2) the signer is a param updater 565 if !reflect.DeepEqual(txn.PublicKey, txMeta.ProfilePublicKey) && !updaterIsParamUpdater { 566 567 return 0, 0, nil, errors.Wrapf( 568 RuleErrorProfilePubKeyNotAuthorized, 569 "_connectUpdateProfile: Profile pub key: %v, signer public key: %v", 570 PkToStringBoth(txn.PublicKey), PkToStringBoth(txMeta.ProfilePublicKey)) 571 } 572 } 573 } 574 575 // If a profile with this username exists already AND if that profile 576 // belongs to another public key then that's an error. 577 if len(txMeta.NewUsername) != 0 { 578 // Note that this check is case-insensitive 579 existingProfileEntry := bav.GetProfileEntryForUsername(txMeta.NewUsername) 580 if existingProfileEntry != nil && !existingProfileEntry.isDeleted && 581 !reflect.DeepEqual(existingProfileEntry.PublicKey, profilePublicKey) { 582 583 return 0, 0, nil, errors.Wrapf( 584 RuleErrorProfileUsernameExists, "Username: %v, TxHashHex: %v", 585 string(txMeta.NewUsername), hex.EncodeToString(txHash[:])) 586 } 587 } 588 589 // Connect basic txn to get the total input and the total output without 590 // considering the transaction metadata. 591 // 592 // The ignoreUtxos flag is used to connect "seed" transactions when initializing 593 // the blockchain. It allows us to "seed" the database with posts and profiles 594 // when we do a hard fork, without having the transactions rejected due to their 595 // not being spendable. 596 var totalInput, totalOutput uint64 597 var utxoOpsForTxn = []*UtxoOperation{} 598 var err error 599 if !ignoreUtxos { 600 totalInput, totalOutput, utxoOpsForTxn, err = bav._connectBasicTransfer( 601 txn, txHash, blockHeight, verifySignatures) 602 if err != nil { 603 return 0, 0, nil, errors.Wrapf(err, "_connectUpdateProfile: ") 604 } 605 606 // Force the input to be non-zero so that we can prevent replay attacks. 607 if totalInput == 0 { 608 return 0, 0, nil, RuleErrorProfileUpdateRequiresNonZeroInput 609 } 610 } 611 612 // See if a profile already exists for this public key. 613 existingProfileEntry := bav.GetProfileEntryForPublicKey(profilePublicKey) 614 // If we are creating a profile for the first time, assess the create profile fee. 615 if existingProfileEntry == nil { 616 createProfileFeeNanos := bav.GlobalParamsEntry.CreateProfileFeeNanos 617 totalOutput += createProfileFeeNanos 618 if totalInput < totalOutput { 619 return 0, 0, nil, RuleErrorCreateProfileTxnOutputExceedsInput 620 } 621 } 622 // Save a copy of the profile entry so so that we can safely modify it. 623 var prevProfileEntry *ProfileEntry 624 if existingProfileEntry != nil { 625 // NOTE: The only pointer in here is the StakeEntry and CoinEntry pointer, but since 626 // this is not modified below we don't need to make a copy of it. 627 prevProfileEntry = &ProfileEntry{} 628 *prevProfileEntry = *existingProfileEntry 629 } 630 631 // This is an adjustment factor that we track for Rosetta. It adjusts 632 // the amount of DeSo to make up for a bug whereby a profile's DeSo locked 633 // could get clobbered during a ParamUpdater txn. 634 clobberedProfileBugDeSoAdjustment := uint64(0) 635 636 // If a profile already exists then we only update fields that are set. 637 var newProfileEntry ProfileEntry 638 if existingProfileEntry != nil && !existingProfileEntry.isDeleted { 639 newProfileEntry = *existingProfileEntry 640 641 // Modifying a profile is only allowed if the transaction public key equals 642 // the profile public key or if the public key belongs to a paramUpdater. 643 _, updaterIsParamUpdater := bav.Params.ParamUpdaterPublicKeys[MakePkMapKey(txn.PublicKey)] 644 if !reflect.DeepEqual(txn.PublicKey, existingProfileEntry.PublicKey) && 645 !updaterIsParamUpdater { 646 647 return 0, 0, nil, errors.Wrapf( 648 RuleErrorProfileModificationNotAuthorized, 649 "_connectUpdateProfile: Profile: %v, profile public key: %v, "+ 650 "txn public key: %v, paramUpdater: %v", existingProfileEntry, 651 PkToStringBoth(existingProfileEntry.PublicKey), 652 PkToStringBoth(txn.PublicKey), spew.Sdump(bav.Params.ParamUpdaterPublicKeys)) 653 } 654 655 // Only set the fields if they have non-zero length. Otherwise leave 656 // them untouched. 657 if len(txMeta.NewUsername) != 0 { 658 newProfileEntry.Username = txMeta.NewUsername 659 } 660 if len(txMeta.NewDescription) != 0 { 661 newProfileEntry.Description = txMeta.NewDescription 662 } 663 if len(txMeta.NewProfilePic) != 0 { 664 newProfileEntry.ProfilePic = txMeta.NewProfilePic 665 } 666 // TODO: Right now a profile can be undeleted by the owner of the profile, 667 // which seems like undesired behavior if a paramUpdater is trying to reduce 668 // spam 669 newProfileEntry.IsHidden = txMeta.IsHidden 670 671 // Just always set the creator basis points and stake multiple. 672 newProfileEntry.CreatorBasisPoints = txMeta.NewCreatorBasisPoints 673 674 // The StakeEntry is always left unmodified here. 675 676 } else { 677 // When there's no pre-existing profile entry we need to do more 678 // checks. 679 if len(txMeta.NewUsername) == 0 { 680 return 0, 0, nil, RuleErrorProfileUsernameTooShort 681 } 682 // We allow users to create profiles without a description or picture 683 // in the consensus code. If desired, frontends can filter out profiles 684 // that don't have these fields. 685 // 686 // Creator percentage and stake multiple are sufficiently checked above. 687 688 // In this case we need to set all the fields using what was passed 689 // into the transaction. 690 691 // If below block height, use transaction public key. 692 // If above block height, use ProfilePublicKey if available. 693 profileEntryPublicKey := txn.PublicKey 694 if blockHeight > ParamUpdaterProfileUpdateFixBlockHeight { 695 profileEntryPublicKey = profilePublicKey 696 } else if !reflect.DeepEqual(txn.PublicKey, txMeta.ProfilePublicKey) { 697 // In this case a clobbering will occur if there was a pre-existing profile 698 // associated with txn.PublicKey. In this case, we save the 699 // DESO locked of the previous profile associated with the 700 // txn.PublicKey. Sorry this is confusing... 701 702 // Look up the profile of the txn.PublicKey 703 clobberedProfileEntry := bav.GetProfileEntryForPublicKey(txn.PublicKey) 704 // Save the amount of DESO locked in the profile since this is going to 705 // be clobbered. 706 if clobberedProfileEntry != nil && !clobberedProfileEntry.isDeleted { 707 clobberedProfileBugDeSoAdjustment = clobberedProfileEntry.CoinEntry.DeSoLockedNanos 708 } 709 } 710 711 newProfileEntry = ProfileEntry{ 712 PublicKey: profileEntryPublicKey, 713 Username: txMeta.NewUsername, 714 Description: txMeta.NewDescription, 715 ProfilePic: txMeta.NewProfilePic, 716 717 CoinEntry: CoinEntry{ 718 CreatorBasisPoints: txMeta.NewCreatorBasisPoints, 719 720 // The other coin fields are automatically set to zero, which is an 721 // appropriate default value for all of them. 722 }, 723 } 724 725 } 726 // At this point the newProfileEntry should be set to what we actually 727 // want to store in the db. 728 729 if verifySignatures { 730 // _connectBasicTransfer has already checked that the transaction is 731 // signed by the top-level public key, which we take to be the poster's 732 // public key. 733 } 734 735 // Delete the old profile mappings. Not doing this could cause a username 736 // change to have outdated mappings, among other things. 737 if prevProfileEntry != nil { 738 bav._deleteProfileEntryMappings(prevProfileEntry) 739 } 740 741 // Save the profile entry now that we've updated it or created it from scratch. 742 bav._setProfileEntryMappings(&newProfileEntry) 743 744 // Add an operation to the list at the end indicating we've updated a profile. 745 utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{ 746 Type: OperationTypeUpdateProfile, 747 PrevProfileEntry: prevProfileEntry, 748 ClobberedProfileBugDESOLockedNanos: clobberedProfileBugDeSoAdjustment, 749 }) 750 751 return totalInput, totalOutput, utxoOpsForTxn, nil 752 } 753 754 func (bav *UtxoView) _connectSwapIdentity( 755 txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) ( 756 _totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) { 757 758 // Check that the transaction has the right TxnType. 759 if txn.TxnMeta.GetTxnType() != TxnTypeSwapIdentity { 760 return 0, 0, nil, fmt.Errorf( 761 "_connectSwapIdentity: called with bad TxnType %s", 762 txn.TxnMeta.GetTxnType().String()) 763 } 764 txMeta := txn.TxnMeta.(*SwapIdentityMetadataa) 765 766 // The txn.PublicKey must be paramUpdater 767 _, updaterIsParamUpdater := bav.Params.ParamUpdaterPublicKeys[MakePkMapKey(txn.PublicKey)] 768 if !updaterIsParamUpdater { 769 return 0, 0, nil, RuleErrorSwapIdentityIsParamUpdaterOnly 770 } 771 772 // call _connectBasicTransfer to verify signatures 773 totalInput, totalOutput, utxoOpsForTxn, err := bav._connectBasicTransfer( 774 txn, txHash, blockHeight, verifySignatures) 775 if err != nil { 776 return 0, 0, nil, errors.Wrapf(err, "_connectSwapIdentity: ") 777 } 778 779 // Force the input to be non-zero so that we can prevent replay attacks. 780 if totalInput == 0 { 781 return 0, 0, nil, RuleErrorProfileUpdateRequiresNonZeroInput 782 } 783 784 // The "from " public key must be set and valid. 785 fromPublicKey := txMeta.FromPublicKey 786 if len(fromPublicKey) != btcec.PubKeyBytesLenCompressed { 787 return 0, 0, nil, RuleErrorFromPublicKeyIsRequired 788 } 789 if _, err := btcec.ParsePubKey(fromPublicKey, btcec.S256()); err != nil { 790 return 0, 0, nil, errors.Wrap(RuleErrorInvalidFromPublicKey, err.Error()) 791 } 792 793 // The "to" public key must be set and valid. 794 toPublicKey := txMeta.ToPublicKey 795 if len(toPublicKey) != btcec.PubKeyBytesLenCompressed { 796 return 0, 0, nil, RuleErrorToPublicKeyIsRequired 797 } 798 if _, err := btcec.ParsePubKey(toPublicKey, btcec.S256()); err != nil { 799 return 0, 0, nil, errors.Wrap(RuleErrorInvalidToPublicKey, err.Error()) 800 } 801 802 if verifySignatures { 803 // _connectBasicTransfer has already checked that the transaction is 804 // signed by the top-level public key, which we take to be the poster's 805 // public key. 806 } 807 808 // If a profile is associated with either of the public keys then change the public 809 // key embedded in the profile. Note that we don't need to delete and re-add the 810 // ProfileEntry mappings because everything other than the embedded public key stays 811 // the same (basically the public key is the only thing that's de-normalized that we 812 // need to manually adjust). Note that we must do this lookup *before* we swap the 813 // PKID's or else we're get opposite profiles back. 814 fromProfileEntry := bav.GetProfileEntryForPublicKey(fromPublicKey) 815 if fromProfileEntry != nil && !fromProfileEntry.isDeleted { 816 fromProfileEntry.PublicKey = toPublicKey 817 } 818 toProfileEntry := bav.GetProfileEntryForPublicKey(toPublicKey) 819 if toProfileEntry != nil && !toProfileEntry.isDeleted { 820 toProfileEntry.PublicKey = fromPublicKey 821 } 822 823 // Get the existing PKID mappings. These are guaranteed to be set (they default to 824 // the existing public key if they are unset). 825 oldFromPKIDEntry := bav.GetPKIDForPublicKey(fromPublicKey) 826 if oldFromPKIDEntry == nil || oldFromPKIDEntry.isDeleted { 827 // This should basically never happen since we never delete PKIDs. 828 return 0, 0, nil, RuleErrorOldFromPublicKeyHasDeletedPKID 829 } 830 oldToPKIDEntry := bav.GetPKIDForPublicKey(toPublicKey) 831 if oldToPKIDEntry == nil || oldToPKIDEntry.isDeleted { 832 // This should basically never happen since we never delete PKIDs. 833 return 0, 0, nil, RuleErrorOldToPublicKeyHasDeletedPKID 834 } 835 836 // At this point, we are certain that the *from* and the *to* public keys 837 // have valid PKID's. 838 839 // Create copies of the old PKID's so we can safely update the mappings. 840 newFromPKIDEntry := *oldFromPKIDEntry 841 newToPKIDEntry := *oldToPKIDEntry 842 843 // Swap the PKID's on the entry copies. 844 newFromPKIDEntry.PKID = oldToPKIDEntry.PKID 845 newToPKIDEntry.PKID = oldFromPKIDEntry.PKID 846 847 // Delete the old mappings for the *from* and *to* PKID's. This isn't really needed 848 // because the calls to _setPKIDMappings below will undo the deletions we just did, 849 // but we do it to maintain consistency with other functions. 850 bav._deletePKIDMappings(oldFromPKIDEntry) 851 bav._deletePKIDMappings(oldToPKIDEntry) 852 853 // Set the new mappings for the *from* and *to* PKID's. 854 bav._setPKIDMappings(&newFromPKIDEntry) 855 bav._setPKIDMappings(&newToPKIDEntry) 856 857 // Postgres doesn't have a concept of PKID Mappings. Instead, we need to save an empty 858 // profile with the correct PKID and public key 859 if bav.Postgres != nil { 860 if fromProfileEntry == nil { 861 bav._setProfileEntryMappings(&ProfileEntry{ 862 PublicKey: toPublicKey, 863 }) 864 } 865 866 if toProfileEntry == nil { 867 bav._setProfileEntryMappings(&ProfileEntry{ 868 PublicKey: fromPublicKey, 869 }) 870 } 871 } 872 873 // Rosetta needs to know the current locked deso in each profile so it can model the swap of 874 // the creator coins. Rosetta models a swap identity as two INPUTs and two OUTPUTs effectively 875 // swapping the balances of total deso locked. If no profile exists, from/to is zero. 876 fromNanos := uint64(0) 877 if fromProfileEntry != nil { 878 fromNanos = fromProfileEntry.CoinEntry.DeSoLockedNanos 879 } 880 toNanos := uint64(0) 881 if toProfileEntry != nil { 882 toNanos = toProfileEntry.CoinEntry.DeSoLockedNanos 883 } 884 885 // Add an operation to the list at the end indicating we've swapped identities. 886 utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{ 887 Type: OperationTypeSwapIdentity, 888 // Rosetta fields 889 SwapIdentityFromDESOLockedNanos: fromNanos, 890 SwapIdentityToDESOLockedNanos: toNanos, 891 892 // Note that we don't need any metadata on this operation, since the swap is reversible 893 // without it. 894 }) 895 896 return totalInput, totalOutput, utxoOpsForTxn, nil 897 } 898 899 // _verifyAccessSignature verifies if the accessSignature is correct. Valid 900 // accessSignature is the signed hash of (derivedPublicKey + expirationBlock) 901 // in DER format, made with the ownerPublicKey. 902 func _verifyAccessSignature(ownerPublicKey []byte, derivedPublicKey []byte, 903 expirationBlock uint64, accessSignature []byte) error { 904 905 // Sanity-check and convert ownerPublicKey to *btcec.PublicKey. 906 if len(ownerPublicKey) != btcec.PubKeyBytesLenCompressed { 907 fmt.Errorf("_verifyAccessSignature: Problem parsing owner public key") 908 } 909 ownerPk, err := btcec.ParsePubKey(ownerPublicKey, btcec.S256()) 910 if err != nil { 911 return errors.Wrapf(err, "_verifyAccessSignature: Problem parsing owner public key: ") 912 } 913 914 // Sanity-check and convert derivedPublicKey to *btcec.PublicKey. 915 if len(derivedPublicKey) != btcec.PubKeyBytesLenCompressed { 916 fmt.Errorf("_verifyAccessSignature: Problem parsing derived public key") 917 } 918 _, err = btcec.ParsePubKey(derivedPublicKey, btcec.S256()) 919 if err != nil { 920 return errors.Wrapf(err, "_verifyAccessSignature: Problem parsing derived public key: ") 921 } 922 923 // Compute a hash of derivedPublicKey+expirationBlock. 924 expirationBlockBytes := EncodeUint64(expirationBlock) 925 accessBytes := append(derivedPublicKey, expirationBlockBytes[:]...) 926 accessHash := Sha256DoubleHash(accessBytes) 927 928 // Convert accessSignature to *btcec.Signature. 929 signature, err := btcec.ParseDERSignature(accessSignature, btcec.S256()) 930 if err != nil { 931 return errors.Wrapf(err, "_verifyAccessSignature: Problem parsing access signature: ") 932 } 933 934 // Verify signature. 935 if !signature.Verify(accessHash[:], ownerPk) { 936 return fmt.Errorf("_verifyAccessSignature: Invalid signature") 937 } 938 939 return nil 940 } 941 942 func (bav *UtxoView) _connectAuthorizeDerivedKey( 943 txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) ( 944 _totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) { 945 946 if blockHeight < NFTTransferOrBurnAndDerivedKeysBlockHeight { 947 return 0, 0, nil, RuleErrorDerivedKeyBeforeBlockHeight 948 } 949 950 // Check that the transaction has the right TxnType. 951 if txn.TxnMeta.GetTxnType() != TxnTypeAuthorizeDerivedKey { 952 return 0, 0, nil, fmt.Errorf("_connectAuthorizeDerivedKey: called with bad TxnType %s", 953 txn.TxnMeta.GetTxnType().String()) 954 } 955 956 txMeta := txn.TxnMeta.(*AuthorizeDerivedKeyMetadata) 957 958 // Validate the operation type. 959 if txMeta.OperationType != AuthorizeDerivedKeyOperationValid && 960 txMeta.OperationType != AuthorizeDerivedKeyOperationNotValid { 961 return 0, 0, nil, fmt.Errorf("_connectAuthorizeDerivedKey: called with bad OperationType %s", 962 txn.TxnMeta.GetTxnType().String()) 963 } 964 965 // Make sure transaction hasn't expired. 966 if txMeta.ExpirationBlock <= uint64(blockHeight) { 967 return 0, 0, nil, RuleErrorAuthorizeDerivedKeyExpiredDerivedPublicKey 968 } 969 970 // Validate the owner public key. 971 ownerPublicKey := txn.PublicKey 972 if len(ownerPublicKey) != btcec.PubKeyBytesLenCompressed { 973 return 0, 0, nil, RuleErrorAuthorizeDerivedKeyInvalidOwnerPublicKey 974 } 975 if _, err := btcec.ParsePubKey(ownerPublicKey, btcec.S256()); err != nil { 976 return 0, 0, nil, errors.Wrap(RuleErrorAuthorizeDerivedKeyInvalidOwnerPublicKey, err.Error()) 977 } 978 979 // Validate the derived public key. 980 derivedPublicKey := txMeta.DerivedPublicKey 981 if len(derivedPublicKey) != btcec.PubKeyBytesLenCompressed { 982 return 0, 0, nil, RuleErrorAuthorizeDerivedKeyInvalidDerivedPublicKey 983 } 984 if _, err := btcec.ParsePubKey(derivedPublicKey, btcec.S256()); err != nil { 985 return 0, 0, nil, errors.Wrap(RuleErrorAuthorizeDerivedKeyInvalidDerivedPublicKey, err.Error()) 986 } 987 988 // Verify that the access signature is valid. This means the derived key is authorized. 989 err := _verifyAccessSignature(ownerPublicKey, derivedPublicKey, 990 txMeta.ExpirationBlock, txMeta.AccessSignature) 991 if err != nil { 992 return 0, 0, nil, errors.Wrap(RuleErrorAuthorizeDerivedKeyAccessSignatureNotValid, err.Error()) 993 } 994 995 // Get current (previous) derived key entry. We might revert to it later so we copy it. 996 prevDerivedKeyEntry := bav._getDerivedKeyMappingForOwner(ownerPublicKey, derivedPublicKey) 997 998 // Authorize transactions can be signed by both owner and derived keys. However, this 999 // poses a risk in a situation where a malicious derived key, which has previously been 1000 // de-authorized by the owner, were to attempt to re-authorize itself. 1001 // To prevent this, the following check completely blocks a derived key once it has been 1002 // de-authorized. This makes the lifecycle of a derived key more controllable. 1003 if prevDerivedKeyEntry != nil && !prevDerivedKeyEntry.isDeleted { 1004 if prevDerivedKeyEntry.OperationType == AuthorizeDerivedKeyOperationNotValid { 1005 return 0, 0, nil, RuleErrorAuthorizeDerivedKeyDeletedDerivedPublicKey 1006 } 1007 } 1008 1009 // At this point we've verified the access signature, which means the derived key is authorized 1010 // to sign on behalf of the owner. In particular, if this authorize transaction was signed 1011 // by the derived key, we would accept it. We accommodate this by adding a temporary derived 1012 // key entry to UtxoView, to support first-time derived keys (they don't exist in the DB yet). 1013 // As a result, and if the derived key is present in transaction's ExtraData, we will 1014 // pass signature verification in _connectBasicTransfer() -> _verifySignature(). 1015 // 1016 // NOTE: Setting a mapping in UtxoView prior to fully validating a transaction shouldn't be 1017 // reproduced elsewhere. It's error-prone, controversial, some even call it "a dirty hack!" 1018 // All considered, this feature greatly simplifies the flow in identity - from the moment you 1019 // generate a derived key, you can use it to sign any transaction offline, including authorize 1020 // transactions. It also resolves issues in situations where the owner account has insufficient 1021 // balance to submit an authorize transaction. 1022 derivedKeyEntry := DerivedKeyEntry{ 1023 OwnerPublicKey: *NewPublicKey(ownerPublicKey), 1024 DerivedPublicKey: *NewPublicKey(derivedPublicKey), 1025 ExpirationBlock: txMeta.ExpirationBlock, 1026 OperationType: AuthorizeDerivedKeyOperationValid, 1027 isDeleted: false, 1028 } 1029 bav._setDerivedKeyMapping(&derivedKeyEntry) 1030 1031 // Call _connectBasicTransfer() to verify txn signature. 1032 totalInput, totalOutput, utxoOpsForTxn, err := bav._connectBasicTransfer( 1033 txn, txHash, blockHeight, verifySignatures) 1034 if err != nil { 1035 // Since we've failed, we revert the UtxoView mapping to what it was previously. 1036 // We're doing this manually because we've set a temporary entry in UtxoView. 1037 bav._deleteDerivedKeyMapping(&derivedKeyEntry) 1038 bav._setDerivedKeyMapping(prevDerivedKeyEntry) 1039 return 0, 0, nil, errors.Wrapf(err, "_connectAuthorizeDerivedKey: ") 1040 } 1041 1042 // Force the input to be non-zero so that we can prevent replay attacks. 1043 if totalInput == 0 { 1044 // Since we've failed, we revert the UtxoView mapping to what it was previously. 1045 // We're doing this manually because we've set a temporary entry in UtxoView. 1046 bav._deleteDerivedKeyMapping(&derivedKeyEntry) 1047 bav._setDerivedKeyMapping(prevDerivedKeyEntry) 1048 return 0, 0, nil, RuleErrorAuthorizeDerivedKeyRequiresNonZeroInput 1049 } 1050 1051 // Earlier we've set a temporary derived key entry that had OperationType set to Valid. 1052 // So if the txn metadata had OperationType set to NotValid, we update the entry here. 1053 bav._deleteDerivedKeyMapping(&derivedKeyEntry) 1054 derivedKeyEntry.OperationType = txMeta.OperationType 1055 bav._setDerivedKeyMapping(&derivedKeyEntry) 1056 1057 if verifySignatures { 1058 // _connectBasicTransfer has already checked that the transaction is 1059 // signed by the owner key or the derived key. 1060 } 1061 1062 // Add an operation to the list at the end indicating we've authorized a derived key. 1063 // Also add the prevDerivedKeyEntry for disconnecting. 1064 utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{ 1065 Type: OperationTypeAuthorizeDerivedKey, 1066 PrevDerivedKeyEntry: prevDerivedKeyEntry, 1067 }) 1068 1069 return totalInput, totalOutput, utxoOpsForTxn, nil 1070 } 1071 1072 func (bav *UtxoView) _disconnectUpdateProfile( 1073 operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash, 1074 utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error { 1075 1076 // Verify that the last operation is an UpdateProfile opration 1077 if len(utxoOpsForTxn) == 0 { 1078 return fmt.Errorf("_disconnectUpdateProfile: utxoOperations are missing") 1079 } 1080 operationIndex := len(utxoOpsForTxn) - 1 1081 currentOperation := utxoOpsForTxn[operationIndex] 1082 if currentOperation.Type != OperationTypeUpdateProfile { 1083 return fmt.Errorf("_disconnectUpdateProfile: Trying to revert "+ 1084 "OperationTypeUpdateProfile but found type %v", 1085 currentOperation.Type) 1086 } 1087 1088 // Now we know the txMeta is UpdateProfile 1089 txMeta := currentTxn.TxnMeta.(*UpdateProfileMetadata) 1090 1091 // Extract the public key of the profile from the meta if necessary and run some 1092 // sanity checks. 1093 profilePublicKey := currentTxn.PublicKey 1094 if len(txMeta.ProfilePublicKey) != 0 { 1095 if len(txMeta.ProfilePublicKey) != btcec.PubKeyBytesLenCompressed { 1096 return fmt.Errorf("_disconnectUpdateProfile: %#v", txMeta.ProfilePublicKey) 1097 } 1098 _, err := btcec.ParsePubKey(txMeta.ProfilePublicKey, btcec.S256()) 1099 if err != nil { 1100 return fmt.Errorf("_disconnectUpdateProfile: %v", err) 1101 } 1102 profilePublicKey = txMeta.ProfilePublicKey 1103 } 1104 1105 // Get the ProfileEntry. If we don't find 1106 // it or if it has isDeleted=true that's an error. 1107 profileEntry := bav.GetProfileEntryForPublicKey(profilePublicKey) 1108 if profileEntry == nil || profileEntry.isDeleted { 1109 return fmt.Errorf("_disconnectUpdateProfile: ProfileEntry for "+ 1110 "public key %v was found to be nil or deleted: %v", 1111 PkToString(profilePublicKey, bav.Params), 1112 profileEntry) 1113 } 1114 1115 // Now that we are confident the ProfileEntry lines up with the transaction we're 1116 // rolling back, set the mappings to be equal to whatever we had previously. 1117 // We need to do this to prevent a fetch from a db later on. 1118 bav._deleteProfileEntryMappings(profileEntry) 1119 1120 // If we had a previous ProfileEntry set then update the mappings to match 1121 // that. Otherwise leave it as deleted. 1122 if currentOperation.PrevProfileEntry != nil { 1123 bav._setProfileEntryMappings(currentOperation.PrevProfileEntry) 1124 } 1125 1126 // Now revert the basic transfer with the remaining operations. Cut off 1127 // the UpdateProfile operation at the end since we just reverted it. 1128 return bav._disconnectBasicTransfer( 1129 currentTxn, txnHash, utxoOpsForTxn[:operationIndex], blockHeight) 1130 } 1131 1132 func (bav *UtxoView) _disconnectSwapIdentity( 1133 operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash, 1134 utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error { 1135 1136 // Verify that the last operation is an SwapIdentity operation 1137 if len(utxoOpsForTxn) == 0 { 1138 return fmt.Errorf("_disconnectSwapIdentity: utxoOperations are missing") 1139 } 1140 operationIndex := len(utxoOpsForTxn) - 1 1141 currentOperation := utxoOpsForTxn[operationIndex] 1142 if currentOperation.Type != OperationTypeSwapIdentity { 1143 return fmt.Errorf("_disconnectSwapIdentity: Trying to revert "+ 1144 "OperationTypeSwapIdentity but found type %v", 1145 currentOperation.Type) 1146 } 1147 1148 // Now we know the txMeta is SwapIdentity 1149 txMeta := currentTxn.TxnMeta.(*SwapIdentityMetadataa) 1150 1151 // Swap the public keys within the profiles back. Note that this *must* be done 1152 // before the swapping of the PKID mappings occurs. Not doing this would cause 1153 // the profiles to be fetched inconsistently from the DB. 1154 fromProfileEntry := bav.GetProfileEntryForPublicKey(txMeta.FromPublicKey) 1155 if fromProfileEntry != nil && !fromProfileEntry.isDeleted { 1156 fromProfileEntry.PublicKey = txMeta.ToPublicKey 1157 } 1158 toProfileEntry := bav.GetProfileEntryForPublicKey(txMeta.ToPublicKey) 1159 if toProfileEntry != nil && !toProfileEntry.isDeleted { 1160 toProfileEntry.PublicKey = txMeta.FromPublicKey 1161 } 1162 1163 // Get the PKIDEntries for the *from* and *to* public keys embedded in the txn 1164 oldFromPKIDEntry := bav.GetPKIDForPublicKey(txMeta.FromPublicKey) 1165 oldToPKIDEntry := bav.GetPKIDForPublicKey(txMeta.ToPublicKey) 1166 1167 // Create copies of the old entries with swapped PKIDs. 1168 newFromPKIDEntry := *oldFromPKIDEntry 1169 newFromPKIDEntry.PKID = oldToPKIDEntry.PKID 1170 1171 newToPKIDEntry := *oldToPKIDEntry 1172 newToPKIDEntry.PKID = oldFromPKIDEntry.PKID 1173 1174 // Delete the old mappings. This isn't strictly necessary since the sets 1175 // below will overwrite everything, but it keeps us be consistent with other code. 1176 bav._deletePKIDMappings(oldFromPKIDEntry) 1177 bav._deletePKIDMappings(oldToPKIDEntry) 1178 1179 // Set the new mappings for the *from* and *to* PKID's. 1180 bav._setPKIDMappings(&newFromPKIDEntry) 1181 bav._setPKIDMappings(&newToPKIDEntry) 1182 1183 // Now revert the basic transfer with the remaining operations. Cut off 1184 // the SwapIdentity operation at the end since we just reverted it. 1185 return bav._disconnectBasicTransfer( 1186 currentTxn, txnHash, utxoOpsForTxn[:operationIndex], blockHeight) 1187 } 1188 1189 func (bav *UtxoView) _disconnectAuthorizeDerivedKey( 1190 operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash, 1191 utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error { 1192 1193 // Verify that the last operation is a AuthorizeDerivedKey operation. 1194 if len(utxoOpsForTxn) == 0 { 1195 return fmt.Errorf("_disconnectAuthorizeDerivedKey: utxoOperations are missing") 1196 } 1197 operationIndex := len(utxoOpsForTxn) - 1 1198 if utxoOpsForTxn[operationIndex].Type != OperationTypeAuthorizeDerivedKey { 1199 return fmt.Errorf("_disconnectAuthorizeDerivedKey: Trying to revert "+ 1200 "OperationTypeAuthorizeDerivedKey but found type %v", 1201 utxoOpsForTxn[operationIndex].Type) 1202 } 1203 1204 txMeta := currentTxn.TxnMeta.(*AuthorizeDerivedKeyMetadata) 1205 prevDerivedKeyEntry := utxoOpsForTxn[operationIndex].PrevDerivedKeyEntry 1206 1207 // Sanity check that txn public key is valid. Assign this public key to ownerPublicKey. 1208 var ownerPublicKey []byte 1209 if len(currentTxn.PublicKey) != btcec.PubKeyBytesLenCompressed { 1210 return fmt.Errorf("_disconnectAuthorizeDerivedKey invalid public key: %v", currentTxn.PublicKey) 1211 } 1212 _, err := btcec.ParsePubKey(currentTxn.PublicKey, btcec.S256()) 1213 if err != nil { 1214 return fmt.Errorf("_disconnectAuthorizeDerivedKey invalid public key: %v", err) 1215 } 1216 ownerPublicKey = currentTxn.PublicKey 1217 1218 // Sanity check that derived key is valid. Assign this key to derivedPublicKey. 1219 var derivedPublicKey []byte 1220 if len(txMeta.DerivedPublicKey) != btcec.PubKeyBytesLenCompressed { 1221 return fmt.Errorf("_disconnectAuthorizeDerivedKey invalid derived key: %v", txMeta.DerivedPublicKey) 1222 } 1223 _, err = btcec.ParsePubKey(txMeta.DerivedPublicKey, btcec.S256()) 1224 if err != nil { 1225 return fmt.Errorf("_disconnectAuthorizeDerivedKey invalid derived key: %v", err) 1226 } 1227 derivedPublicKey = txMeta.DerivedPublicKey 1228 1229 // Get the derived key entry. If it's nil or is deleted then we have an error. 1230 derivedKeyEntry := bav._getDerivedKeyMappingForOwner(ownerPublicKey, derivedPublicKey) 1231 if derivedKeyEntry == nil || derivedKeyEntry.isDeleted { 1232 return fmt.Errorf("_disconnectAuthorizeDerivedKey: DerivedKeyEntry for "+ 1233 "public key %v, derived key %v was found to be nil or deleted: %v", 1234 PkToString(ownerPublicKey, bav.Params), PkToString(derivedPublicKey, bav.Params), 1235 derivedKeyEntry) 1236 } 1237 1238 // If we had a previous derivedKeyEntry set then compare it with the current entry. 1239 if prevDerivedKeyEntry != nil { 1240 // Sanity check public keys. This should never fail. 1241 if !reflect.DeepEqual(ownerPublicKey, prevDerivedKeyEntry.OwnerPublicKey[:]) { 1242 return fmt.Errorf("_disconnectAuthorizeDerivedKey: Owner public key in txn "+ 1243 "differs from that in previous derivedKeyEntry (%v %v)", prevDerivedKeyEntry.OwnerPublicKey, ownerPublicKey) 1244 } 1245 if !reflect.DeepEqual(derivedPublicKey, prevDerivedKeyEntry.DerivedPublicKey[:]) { 1246 return fmt.Errorf("_disconnectAuthorizeDerivedKey: Derived public key in txn "+ 1247 "differs from that in existing derivedKeyEntry (%v %v)", prevDerivedKeyEntry.DerivedPublicKey, derivedPublicKey) 1248 } 1249 } 1250 1251 // Now that we are confident the derivedKeyEntry lines up with the transaction we're 1252 // rolling back, delete the mapping from utxoView. We need to do this to prevent 1253 // a fetch from a db later on. 1254 bav._deleteDerivedKeyMapping(derivedKeyEntry) 1255 1256 // Set the previous derivedKeyEntry. 1257 bav._setDerivedKeyMapping(prevDerivedKeyEntry) 1258 1259 // Now revert the basic transfer with the remaining operations. Cut off 1260 // the authorizeDerivedKey operation at the end since we just reverted it. 1261 return bav._disconnectBasicTransfer( 1262 currentTxn, txnHash, utxoOpsForTxn[:operationIndex], blockHeight) 1263 }