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  }