github.com/deso-protocol/core@v1.2.9/lib/block_view_creator_coin.go (about)

     1  package lib
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/btcsuite/btcd/btcec"
     6  	"github.com/golang/glog"
     7  	"github.com/pkg/errors"
     8  	"math"
     9  	"math/big"
    10  	"reflect"
    11  )
    12  
    13  func (bav *UtxoView) _getBalanceEntryForHODLerPKIDAndCreatorPKID(
    14  	hodlerPKID *PKID, creatorPKID *PKID) *BalanceEntry {
    15  
    16  	// If an entry exists in the in-memory map, return the value of that mapping.
    17  	balanceEntryKey := MakeCreatorCoinBalanceKey(hodlerPKID, creatorPKID)
    18  	mapValue, existsMapValue := bav.HODLerPKIDCreatorPKIDToBalanceEntry[balanceEntryKey]
    19  	if existsMapValue {
    20  		return mapValue
    21  	}
    22  
    23  	// If we get here it means no value exists in our in-memory map. In this case,
    24  	// defer to the db. If a mapping exists in the db, return it. If not, return
    25  	// nil.
    26  	var balanceEntry *BalanceEntry
    27  	if bav.Postgres != nil {
    28  		balance := bav.Postgres.GetCreatorCoinBalance(hodlerPKID, creatorPKID)
    29  		if balance != nil {
    30  			balanceEntry = &BalanceEntry{
    31  				HODLerPKID:   balance.HolderPKID,
    32  				CreatorPKID:  balance.CreatorPKID,
    33  				BalanceNanos: balance.BalanceNanos,
    34  				HasPurchased: balance.HasPurchased,
    35  			}
    36  		}
    37  	} else {
    38  		balanceEntry = DBGetCreatorCoinBalanceEntryForHODLerAndCreatorPKIDs(bav.Handle, hodlerPKID, creatorPKID)
    39  	}
    40  	if balanceEntry != nil {
    41  		bav._setBalanceEntryMappingsWithPKIDs(balanceEntry, hodlerPKID, creatorPKID)
    42  	}
    43  	return balanceEntry
    44  }
    45  
    46  func (bav *UtxoView) GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
    47  	hodlerPubKey []byte, creatorPubKey []byte) (
    48  	_balanceEntry *BalanceEntry, _hodlerPKID *PKID, _creatorPKID *PKID) {
    49  
    50  	// These are guaranteed to be non-nil as long as the public keys are valid.
    51  	hodlerPKID := bav.GetPKIDForPublicKey(hodlerPubKey)
    52  	creatorPKID := bav.GetPKIDForPublicKey(creatorPubKey)
    53  
    54  	return bav._getBalanceEntryForHODLerPKIDAndCreatorPKID(hodlerPKID.PKID, creatorPKID.PKID), hodlerPKID.PKID, creatorPKID.PKID
    55  }
    56  
    57  func (bav *UtxoView) _setBalanceEntryMappingsWithPKIDs(
    58  	balanceEntry *BalanceEntry, hodlerPKID *PKID, creatorPKID *PKID) {
    59  
    60  	// This function shouldn't be called with nil.
    61  	if balanceEntry == nil {
    62  		glog.Errorf("_setBalanceEntryMappings: Called with nil BalanceEntry; " +
    63  			"this should never happen.")
    64  		return
    65  	}
    66  
    67  	// Add a mapping for the BalancEntry.
    68  	balanceEntryKey := MakeCreatorCoinBalanceKey(hodlerPKID, creatorPKID)
    69  	bav.HODLerPKIDCreatorPKIDToBalanceEntry[balanceEntryKey] = balanceEntry
    70  }
    71  
    72  func (bav *UtxoView) _setBalanceEntryMappings(
    73  	balanceEntry *BalanceEntry) {
    74  
    75  	bav._setBalanceEntryMappingsWithPKIDs(
    76  		balanceEntry, balanceEntry.HODLerPKID, balanceEntry.CreatorPKID)
    77  }
    78  
    79  func (bav *UtxoView) _deleteBalanceEntryMappingsWithPKIDs(
    80  	balanceEntry *BalanceEntry, hodlerPKID *PKID, creatorPKID *PKID) {
    81  
    82  	// Create a tombstone entry.
    83  	tombstoneBalanceEntry := *balanceEntry
    84  	tombstoneBalanceEntry.isDeleted = true
    85  
    86  	// Set the mappings to point to the tombstone entry.
    87  	bav._setBalanceEntryMappingsWithPKIDs(&tombstoneBalanceEntry, hodlerPKID, creatorPKID)
    88  }
    89  
    90  func (bav *UtxoView) _deleteBalanceEntryMappings(
    91  	balanceEntry *BalanceEntry, hodlerPublicKey []byte, creatorPublicKey []byte) {
    92  
    93  	// These are guaranteed to be non-nil as long as the public keys are valid.
    94  	hodlerPKID := bav.GetPKIDForPublicKey(hodlerPublicKey)
    95  	creatorPKID := bav.GetPKIDForPublicKey(creatorPublicKey)
    96  
    97  	// Set the mappings to point to the tombstone entry.
    98  	bav._deleteBalanceEntryMappingsWithPKIDs(balanceEntry, hodlerPKID.PKID, creatorPKID.PKID)
    99  }
   100  
   101  func (bav *UtxoView) GetHoldings(pkid *PKID, fetchProfiles bool) ([]*BalanceEntry, []*ProfileEntry, error) {
   102  	var entriesYouHold []*BalanceEntry
   103  	if bav.Postgres != nil {
   104  		balances := bav.Postgres.GetHoldings(pkid)
   105  		for _, balance := range balances {
   106  			entriesYouHold = append(entriesYouHold, balance.NewBalanceEntry())
   107  		}
   108  	} else {
   109  		holdings, err := DbGetBalanceEntriesYouHold(bav.Handle, pkid, true)
   110  		if err != nil {
   111  			return nil, nil, err
   112  		}
   113  		entriesYouHold = holdings
   114  	}
   115  
   116  	holdingsMap := make(map[PKID]*BalanceEntry)
   117  	for _, balanceEntry := range entriesYouHold {
   118  		holdingsMap[*balanceEntry.CreatorPKID] = balanceEntry
   119  	}
   120  
   121  	for _, balanceEntry := range bav.HODLerPKIDCreatorPKIDToBalanceEntry {
   122  		if reflect.DeepEqual(balanceEntry.HODLerPKID, pkid) {
   123  			if _, ok := holdingsMap[*balanceEntry.CreatorPKID]; ok {
   124  				// We found both a mempool and a db balanceEntry. Update the BalanceEntry using mempool data.
   125  				holdingsMap[*balanceEntry.CreatorPKID].BalanceNanos = balanceEntry.BalanceNanos
   126  			} else {
   127  				// Add new entries to the list
   128  				entriesYouHold = append(entriesYouHold, balanceEntry)
   129  			}
   130  		}
   131  	}
   132  
   133  	// Optionally fetch all the profile entries as well.
   134  	var profilesYouHold []*ProfileEntry
   135  	if fetchProfiles {
   136  		for _, balanceEntry := range entriesYouHold {
   137  			// In this case you're the hodler so the creator is the one whose profile we need to fetch.
   138  			currentProfileEntry := bav.GetProfileEntryForPKID(balanceEntry.CreatorPKID)
   139  			profilesYouHold = append(profilesYouHold, currentProfileEntry)
   140  		}
   141  	}
   142  
   143  	return entriesYouHold, profilesYouHold, nil
   144  }
   145  
   146  func (bav *UtxoView) GetHolders(pkid *PKID, fetchProfiles bool) ([]*BalanceEntry, []*ProfileEntry, error) {
   147  	var holderEntries []*BalanceEntry
   148  	if bav.Postgres != nil {
   149  		balances := bav.Postgres.GetHolders(pkid)
   150  		for _, balance := range balances {
   151  			holderEntries = append(holderEntries, balance.NewBalanceEntry())
   152  		}
   153  	} else {
   154  		holders, err := DbGetBalanceEntriesHodlingYou(bav.Handle, pkid, true)
   155  		if err != nil {
   156  			return nil, nil, err
   157  		}
   158  		holderEntries = holders
   159  	}
   160  
   161  	holdersMap := make(map[PKID]*BalanceEntry)
   162  	for _, balanceEntry := range holderEntries {
   163  		holdersMap[*balanceEntry.HODLerPKID] = balanceEntry
   164  	}
   165  
   166  	for _, balanceEntry := range bav.HODLerPKIDCreatorPKIDToBalanceEntry {
   167  		if reflect.DeepEqual(balanceEntry.HODLerPKID, pkid) {
   168  			if _, ok := holdersMap[*balanceEntry.HODLerPKID]; ok {
   169  				// We found both a mempool and a db balanceEntry. Update the BalanceEntry using mempool data.
   170  				holdersMap[*balanceEntry.HODLerPKID].BalanceNanos = balanceEntry.BalanceNanos
   171  			} else {
   172  				// Add new entries to the list
   173  				holderEntries = append(holderEntries, balanceEntry)
   174  			}
   175  		}
   176  	}
   177  
   178  	// Optionally fetch all the profile entries as well.
   179  	var profilesYouHold []*ProfileEntry
   180  	if fetchProfiles {
   181  		for _, balanceEntry := range holderEntries {
   182  			// In this case you're the hodler so the creator is the one whose profile we need to fetch.
   183  			currentProfileEntry := bav.GetProfileEntryForPKID(balanceEntry.CreatorPKID)
   184  			profilesYouHold = append(profilesYouHold, currentProfileEntry)
   185  		}
   186  	}
   187  
   188  	return holderEntries, profilesYouHold, nil
   189  }
   190  
   191  func CalculateCreatorCoinToMintPolynomial(
   192  	deltaDeSoNanos uint64, currentCreatorCoinSupplyNanos uint64, params *DeSoParams) uint64 {
   193  	// The values our equations take are generally in whole units rather than
   194  	// nanos, so the first step is to convert the nano amounts into floats
   195  	// representing full coin units.
   196  	bigNanosPerUnit := NewFloat().SetUint64(NanosPerUnit)
   197  	bigDeltaDeSo := Div(NewFloat().SetUint64(deltaDeSoNanos), bigNanosPerUnit)
   198  	bigCurrentCreatorCoinSupply :=
   199  		Div(NewFloat().SetUint64(currentCreatorCoinSupplyNanos), bigNanosPerUnit)
   200  
   201  	// These calculations are basically what you get when you integrate a
   202  	// polynomial price curve. For more information, see the comment on
   203  	// CreatorCoinSlope in constants.go and check out the Mathematica notebook
   204  	// linked in that comment.
   205  	//
   206  	// This is the formula:
   207  	// - (((dB + m*RR*s^(1/RR))/(m*RR)))^RR-s
   208  	// - where:
   209  	//     dB = bigDeltaDeSo,
   210  	//     m = params.CreatorCoinSlope
   211  	//     RR = params.CreatorCoinReserveRatio
   212  	//     s = bigCurrentCreatorCoinSupply
   213  	//
   214  	// If you think it's hard to understand the code below, don't worry-- I hate
   215  	// the Go float libary syntax too...
   216  	bigRet := Sub(BigFloatPow((Div((Add(bigDeltaDeSo,
   217  		Mul(params.CreatorCoinSlope, Mul(params.CreatorCoinReserveRatio,
   218  			BigFloatPow(bigCurrentCreatorCoinSupply, (Div(bigOne,
   219  				params.CreatorCoinReserveRatio))))))), Mul(params.CreatorCoinSlope,
   220  		params.CreatorCoinReserveRatio))), params.CreatorCoinReserveRatio),
   221  		bigCurrentCreatorCoinSupply)
   222  	// The value we get is generally a number of whole creator coins, and so we
   223  	// need to convert it to "nanos" as a last step.
   224  	retNanos, _ := Mul(bigRet, bigNanosPerUnit).Uint64()
   225  	return retNanos
   226  }
   227  
   228  func CalculateCreatorCoinToMintBancor(
   229  	deltaDeSoNanos uint64, currentCreatorCoinSupplyNanos uint64,
   230  	currentDeSoLockedNanos uint64, params *DeSoParams) uint64 {
   231  	// The values our equations take are generally in whole units rather than
   232  	// nanos, so the first step is to convert the nano amounts into floats
   233  	// representing full coin units.
   234  	bigNanosPerUnit := NewFloat().SetUint64(NanosPerUnit)
   235  	bigDeltaDeSo := Div(NewFloat().SetUint64(deltaDeSoNanos), bigNanosPerUnit)
   236  	bigCurrentCreatorCoinSupply := Div(NewFloat().SetUint64(currentCreatorCoinSupplyNanos), bigNanosPerUnit)
   237  	bigCurrentDeSoLocked := Div(NewFloat().SetUint64(currentDeSoLockedNanos), bigNanosPerUnit)
   238  
   239  	// These calculations are derived from the Bancor pricing formula, which
   240  	// is proportional to a polynomial price curve (and equivalent to Uniswap
   241  	// under certain assumptions). For more information, see the comment on
   242  	// CreatorCoinSlope in constants.go and check out the Mathematica notebook
   243  	// linked in that comment.
   244  	//
   245  	// This is the formula:
   246  	// - S0 * ((1 + dB / B0) ^ (RR) - 1)
   247  	// - where:
   248  	//     dB = bigDeltaDeSo,
   249  	//     B0 = bigCurrentDeSoLocked
   250  	//     S0 = bigCurrentCreatorCoinSupply
   251  	//     RR = params.CreatorCoinReserveRatio
   252  	//
   253  	// Sorry the code for the equation is so hard to read.
   254  	bigRet := Mul(bigCurrentCreatorCoinSupply,
   255  		Sub(BigFloatPow((Add(bigOne, Div(bigDeltaDeSo,
   256  			bigCurrentDeSoLocked))),
   257  			(params.CreatorCoinReserveRatio)), bigOne))
   258  	// The value we get is generally a number of whole creator coins, and so we
   259  	// need to convert it to "nanos" as a last step.
   260  	retNanos, _ := Mul(bigRet, bigNanosPerUnit).Uint64()
   261  	return retNanos
   262  }
   263  
   264  func CalculateDeSoToReturn(
   265  	deltaCreatorCoinNanos uint64, currentCreatorCoinSupplyNanos uint64,
   266  	currentDeSoLockedNanos uint64, params *DeSoParams) uint64 {
   267  	// The values our equations take are generally in whole units rather than
   268  	// nanos, so the first step is to convert the nano amounts into floats
   269  	// representing full coin units.
   270  	bigNanosPerUnit := NewFloat().SetUint64(NanosPerUnit)
   271  	bigDeltaCreatorCoin := Div(NewFloat().SetUint64(deltaCreatorCoinNanos), bigNanosPerUnit)
   272  	bigCurrentCreatorCoinSupply := Div(NewFloat().SetUint64(currentCreatorCoinSupplyNanos), bigNanosPerUnit)
   273  	bigCurrentDeSoLocked := Div(NewFloat().SetUint64(currentDeSoLockedNanos), bigNanosPerUnit)
   274  
   275  	// These calculations are derived from the Bancor pricing formula, which
   276  	// is proportional to a polynomial price curve (and equivalent to Uniswap
   277  	// under certain assumptions). For more information, see the comment on
   278  	// CreatorCoinSlope in constants.go and check out the Mathematica notebook
   279  	// linked in that comment.
   280  	//
   281  	// This is the formula:
   282  	// - B0 * (1 - (1 - dS / S0)^(1/RR))
   283  	// - where:
   284  	//     dS = bigDeltaCreatorCoin,
   285  	//     B0 = bigCurrentDeSoLocked
   286  	//     S0 = bigCurrentCreatorCoinSupply
   287  	//     RR = params.CreatorCoinReserveRatio
   288  	//
   289  	// Sorry the code for the equation is so hard to read.
   290  	bigRet := Mul(bigCurrentDeSoLocked, (Sub(bigOne, BigFloatPow((Sub(bigOne,
   291  		Div(bigDeltaCreatorCoin, bigCurrentCreatorCoinSupply))), (Div(bigOne,
   292  		params.CreatorCoinReserveRatio))))))
   293  	// The value we get is generally a number of whole creator coins, and so we
   294  	// need to convert it to "nanos" as a last step.
   295  	retNanos, _ := Mul(bigRet, bigNanosPerUnit).Uint64()
   296  	return retNanos
   297  }
   298  
   299  func CalculateCreatorCoinToMint(
   300  	desoToSellNanos uint64,
   301  	coinsInCirculationNanos uint64, desoLockedNanos uint64,
   302  	params *DeSoParams) uint64 {
   303  
   304  	if desoLockedNanos == 0 {
   305  		// In this case, there is no DeSo in the profile so we have to use
   306  		// the polynomial equations to initialize the coin and determine how
   307  		// much to mint.
   308  		return CalculateCreatorCoinToMintPolynomial(
   309  			desoToSellNanos, coinsInCirculationNanos,
   310  			params)
   311  	}
   312  
   313  	// In this case, we have DeSo locked in the profile and so we use the
   314  	// standard Bancor equations to determine how much creator coin to mint.
   315  	return CalculateCreatorCoinToMintBancor(
   316  		desoToSellNanos, coinsInCirculationNanos,
   317  		desoLockedNanos, params)
   318  }
   319  
   320  func (bav *UtxoView) ValidateDiamondsAndGetNumCreatorCoinNanos(
   321  	senderPublicKey []byte,
   322  	receiverPublicKey []byte,
   323  	diamondPostHash *BlockHash,
   324  	diamondLevel int64,
   325  	blockHeight uint32,
   326  ) (_numCreatorCoinNanos uint64, _netNewDiamonds int64, _err error) {
   327  
   328  	// Check that the diamond level is reasonable
   329  	diamondLevelMap := GetDeSoNanosDiamondLevelMapAtBlockHeight(int64(blockHeight))
   330  	if _, isAllowedLevel := diamondLevelMap[diamondLevel]; !isAllowedLevel {
   331  		return 0, 0, fmt.Errorf(
   332  			"ValidateDiamondsAndGetNumCreatorCoinNanos: Diamond level %v not allowed",
   333  			diamondLevel)
   334  	}
   335  
   336  	// Convert pub keys into PKIDs.
   337  	senderPKID := bav.GetPKIDForPublicKey(senderPublicKey)
   338  	receiverPKID := bav.GetPKIDForPublicKey(receiverPublicKey)
   339  
   340  	// Look up if there is an existing diamond entry.
   341  	diamondKey := MakeDiamondKey(senderPKID.PKID, receiverPKID.PKID, diamondPostHash)
   342  	diamondEntry := bav.GetDiamondEntryForDiamondKey(&diamondKey)
   343  
   344  	// Look up if there's an existing profile entry for the sender. There needs
   345  	// to be in order to be able to give one's creator coin as a diamond.
   346  	existingProfileEntry := bav.GetProfileEntryForPKID(senderPKID.PKID)
   347  	if existingProfileEntry == nil || existingProfileEntry.isDeleted {
   348  		return 0, 0, fmt.Errorf(
   349  			"ValidateDiamondsAndGetNumCreatorCoinNanos: Cannot send CreatorCoin "+
   350  				"with diamond because ProfileEntry for public key %v does not exist",
   351  			senderPublicKey)
   352  	}
   353  	// If we get here, then we're sure the ProfileEntry for this user exists.
   354  
   355  	currDiamondLevel := int64(0)
   356  	if diamondEntry != nil {
   357  		currDiamondLevel = diamondEntry.DiamondLevel
   358  	}
   359  
   360  	if currDiamondLevel >= diamondLevel {
   361  		return 0, 0, RuleErrorCreatorCoinTransferPostAlreadyHasSufficientDiamonds
   362  	}
   363  
   364  	// Calculate the number of creator coin nanos needed vs. already added for previous diamonds.
   365  	currCreatorCoinNanos := GetCreatorCoinNanosForDiamondLevelAtBlockHeight(
   366  		existingProfileEntry.CoinsInCirculationNanos, existingProfileEntry.DeSoLockedNanos,
   367  		currDiamondLevel, int64(blockHeight), bav.Params)
   368  	neededCreatorCoinNanos := GetCreatorCoinNanosForDiamondLevelAtBlockHeight(
   369  		existingProfileEntry.CoinsInCirculationNanos, existingProfileEntry.DeSoLockedNanos,
   370  		diamondLevel, int64(blockHeight), bav.Params)
   371  
   372  	// There is an edge case where, if the person's creator coin value goes down
   373  	// by a large enough amount, then they can get a "free" diamond upgrade. This
   374  	// seems fine for now.
   375  	creatorCoinToTransferNanos := uint64(0)
   376  	if neededCreatorCoinNanos > currCreatorCoinNanos {
   377  		creatorCoinToTransferNanos = neededCreatorCoinNanos - currCreatorCoinNanos
   378  	}
   379  
   380  	netNewDiamonds := diamondLevel - currDiamondLevel
   381  
   382  	return creatorCoinToTransferNanos, netNewDiamonds, nil
   383  }
   384  
   385  func (bav *UtxoView) _disconnectCreatorCoin(
   386  	operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash,
   387  	utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error {
   388  
   389  	// Verify that the last operation is a CreatorCoin opration
   390  	if len(utxoOpsForTxn) == 0 {
   391  		return fmt.Errorf("_disconnectCreatorCoin: utxoOperations are missing")
   392  	}
   393  	operationIndex := len(utxoOpsForTxn) - 1
   394  	if utxoOpsForTxn[operationIndex].Type != OperationTypeCreatorCoin {
   395  		return fmt.Errorf("_disconnectCreatorCoin: Trying to revert "+
   396  			"OperationTypeCreatorCoin but found type %v",
   397  			utxoOpsForTxn[operationIndex].Type)
   398  	}
   399  	txMeta := currentTxn.TxnMeta.(*CreatorCoinMetadataa)
   400  	operationData := utxoOpsForTxn[operationIndex]
   401  	operationIndex--
   402  
   403  	// We sometimes have some extra AddUtxo operations we need to remove
   404  	// These are "implicit" outputs that always occur at the end of the
   405  	// list of UtxoOperations. The number of implicit outputs is equal to
   406  	// the total number of "Add" operations minus the explicit outputs.
   407  	numUtxoAdds := 0
   408  	for _, utxoOp := range utxoOpsForTxn {
   409  		if utxoOp.Type == OperationTypeAddUtxo {
   410  			numUtxoAdds += 1
   411  		}
   412  	}
   413  	operationIndex -= numUtxoAdds - len(currentTxn.TxOutputs)
   414  
   415  	// Get the profile corresponding to the creator coin txn.
   416  	existingProfileEntry := bav.GetProfileEntryForPublicKey(txMeta.ProfilePublicKey)
   417  	// Sanity-check that it exists.
   418  	if existingProfileEntry == nil || existingProfileEntry.isDeleted {
   419  		return fmt.Errorf("_disconnectCreatorCoin: CreatorCoin profile for "+
   420  			"public key %v doesn't exist; this should never happen",
   421  			PkToStringBoth(txMeta.ProfilePublicKey))
   422  	}
   423  	// Get the BalanceEntry of the transactor. This should always exist.
   424  	transactorBalanceEntry, _, _ := bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
   425  		currentTxn.PublicKey, txMeta.ProfilePublicKey)
   426  	// Sanity-check that the transactor BalanceEntry exists
   427  	if transactorBalanceEntry == nil || transactorBalanceEntry.isDeleted {
   428  		return fmt.Errorf("_disconnectCreatorCoin: Transactor BalanceEntry "+
   429  			"pubkey %v and creator pubkey %v does not exist; this should "+
   430  			"never happen",
   431  			PkToStringBoth(currentTxn.PublicKey), PkToStringBoth(txMeta.ProfilePublicKey))
   432  	}
   433  
   434  	// Get the BalanceEntry of the creator. It could be nil if this is a sell
   435  	// transaction or if the balance entry was deleted by a creator coin transfer.
   436  	creatorBalanceEntry, _, _ := bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
   437  		txMeta.ProfilePublicKey, txMeta.ProfilePublicKey)
   438  	if creatorBalanceEntry == nil || creatorBalanceEntry.isDeleted {
   439  		creatorPKID := bav.GetPKIDForPublicKey(txMeta.ProfilePublicKey)
   440  		creatorBalanceEntry = &BalanceEntry{
   441  			HODLerPKID:   creatorPKID.PKID,
   442  			CreatorPKID:  creatorPKID.PKID,
   443  			BalanceNanos: uint64(0),
   444  		}
   445  	}
   446  
   447  	if txMeta.OperationType == CreatorCoinOperationTypeBuy {
   448  		// Set up some variables so that we can run some sanity-checks
   449  		deltaBuyerNanos := transactorBalanceEntry.BalanceNanos - operationData.PrevTransactorBalanceEntry.BalanceNanos
   450  		deltaCreatorNanos := creatorBalanceEntry.BalanceNanos - operationData.PrevCreatorBalanceEntry.BalanceNanos
   451  		deltaCoinsInCirculation := existingProfileEntry.CoinsInCirculationNanos - operationData.PrevCoinEntry.CoinsInCirculationNanos
   452  
   453  		// If the creator is distinct from the buyer, then reset their balance.
   454  		// This check avoids double-updating in situations where a creator bought
   455  		// their own coin.
   456  		if !reflect.DeepEqual(currentTxn.PublicKey, txMeta.ProfilePublicKey) {
   457  
   458  			// Sanity-check that the amount that we increased the CoinsInCirculation by
   459  			// equals the total amount received by the buyer and the creator.
   460  			if deltaBuyerNanos+deltaCreatorNanos != deltaCoinsInCirculation {
   461  				return fmt.Errorf("_disconnectCreatorCoin: The creator coin nanos "+
   462  					"the buyer and the creator received (%v, %v) does not equal the "+
   463  					"creator coins added to the circulating supply %v",
   464  					deltaBuyerNanos, deltaCreatorNanos, deltaCoinsInCirculation)
   465  			}
   466  
   467  			// Sanity-check that the watermark delta equates to what the creator received.
   468  			deltaNanos := uint64(0)
   469  			if blockHeight > DeSoFounderRewardBlockHeight {
   470  				// Do nothing.  After the DeSoFounderRewardBlockHeight, creator coins are not
   471  				// minted as a founder's reward, just DeSo (see utxo reverted later).
   472  			} else if blockHeight > SalomonFixBlockHeight {
   473  				// Following the SalomonFixBlockHeight block, we calculate a founders reward
   474  				// on every buy, not just the ones that push a creator to a new all time high.
   475  				deltaNanos = existingProfileEntry.CoinsInCirculationNanos - operationData.PrevCoinEntry.CoinsInCirculationNanos
   476  			} else {
   477  				// Prior to the SalomonFixBlockHeight block, we calculate the founders reward
   478  				// only for new all time highs.
   479  				deltaNanos = existingProfileEntry.CoinWatermarkNanos - operationData.PrevCoinEntry.CoinWatermarkNanos
   480  			}
   481  			founderRewardNanos := IntDiv(
   482  				IntMul(
   483  					big.NewInt(int64(deltaNanos)),
   484  					big.NewInt(int64(existingProfileEntry.CreatorBasisPoints))),
   485  				big.NewInt(100*100)).Uint64()
   486  			if founderRewardNanos != deltaCreatorNanos {
   487  				return fmt.Errorf("_disconnectCreatorCoin: The creator coin nanos "+
   488  					"the creator received %v does not equal the founder reward %v; "+
   489  					"this should never happen",
   490  					deltaCreatorNanos, founderRewardNanos)
   491  			}
   492  
   493  			// Reset the creator's BalanceEntry to what it was previously.
   494  			*creatorBalanceEntry = *operationData.PrevCreatorBalanceEntry
   495  			bav._setBalanceEntryMappings(creatorBalanceEntry)
   496  		} else {
   497  			// We do a simliar sanity-check as above, but in this case we don't need to
   498  			// reset the creator mappings.
   499  			deltaBuyerNanos := transactorBalanceEntry.BalanceNanos - operationData.PrevTransactorBalanceEntry.BalanceNanos
   500  			deltaCoinsInCirculation := existingProfileEntry.CoinsInCirculationNanos - operationData.PrevCoinEntry.CoinsInCirculationNanos
   501  			if deltaBuyerNanos != deltaCoinsInCirculation {
   502  				return fmt.Errorf("_disconnectCreatorCoin: The creator coin nanos "+
   503  					"the buyer/creator received (%v) does not equal the "+
   504  					"creator coins added to the circulating supply %v",
   505  					deltaBuyerNanos, deltaCoinsInCirculation)
   506  			}
   507  		}
   508  
   509  		// Reset the Buyer's BalanceEntry to what it was previously.
   510  		*transactorBalanceEntry = *operationData.PrevTransactorBalanceEntry
   511  		bav._setBalanceEntryMappings(transactorBalanceEntry)
   512  
   513  		// If a DeSo founder reward was created, revert it.
   514  		if operationData.FounderRewardUtxoKey != nil {
   515  			if err := bav._unAddUtxo(operationData.FounderRewardUtxoKey); err != nil {
   516  				return errors.Wrapf(err, "_disconnectBitcoinExchange: Problem unAdding utxo %v: ", operationData.FounderRewardUtxoKey)
   517  			}
   518  		}
   519  
   520  		// The buyer will get the DeSo they locked up back when we revert the
   521  		// basic transfer. This is OK because resetting the CoinEntry to the previous
   522  		// value lowers the amount of DeSo locked in the profile by the same
   523  		// amount the buyer will receive. Thus no DeSo is created in this
   524  		// transaction.
   525  	} else if txMeta.OperationType == CreatorCoinOperationTypeSell {
   526  		// Set up some variables so that we can run some sanity-checks. The coins
   527  		// the transactor has and the coins in circulation should both have gone
   528  		// down as a result of the transaction, so both of these values should be
   529  		// positive.
   530  		deltaCoinNanos := operationData.PrevTransactorBalanceEntry.BalanceNanos - transactorBalanceEntry.BalanceNanos
   531  		deltaCoinsInCirculation := operationData.PrevCoinEntry.CoinsInCirculationNanos - existingProfileEntry.CoinsInCirculationNanos
   532  
   533  		// Sanity-check that the amount we decreased CoinsInCirculation by
   534  		// equals the total amount put in by the seller.
   535  		if deltaCoinNanos != deltaCoinsInCirculation {
   536  			return fmt.Errorf("_disconnectCreatorCoin: The creator coin nanos "+
   537  				"the seller put in (%v) does not equal the "+
   538  				"creator coins removed from the circulating supply %v",
   539  				deltaCoinNanos, deltaCoinsInCirculation)
   540  		}
   541  
   542  		// In the case of a sell we only need to revert the transactor's balance,
   543  		// and we don't have to worry about the creator's balance.
   544  		// Reset the transactor's BalanceEntry to what it was previously.
   545  		*transactorBalanceEntry = *operationData.PrevTransactorBalanceEntry
   546  		bav._setBalanceEntryMappings(transactorBalanceEntry)
   547  
   548  		// Un-add the UTXO taht was created as a result of this transaction. It should
   549  		// be the one at the end of our UTXO list at this point.
   550  		//
   551  		// The UtxoKey is simply the transaction hash with index set to the end of the
   552  		// transaction list.
   553  		utxoKey := UtxoKey{
   554  			TxID: *currentTxn.Hash(),
   555  			// We give all UTXOs that are created as a result of BitcoinExchange transactions
   556  			// an index of zero. There is generally only one UTXO created in a BitcoinExchange
   557  			// transaction so this field doesn't really matter.
   558  			Index: uint32(len(currentTxn.TxOutputs)),
   559  		}
   560  		if err := bav._unAddUtxo(&utxoKey); err != nil {
   561  			return errors.Wrapf(err, "_disconnectBitcoinExchange: Problem unAdding utxo %v: ", utxoKey)
   562  		}
   563  	} else if txMeta.OperationType == CreatorCoinOperationTypeAddDeSo {
   564  		return fmt.Errorf("_disconnectCreatorCoin: Add DeSo operation txn not implemented")
   565  	}
   566  
   567  	// Reset the CoinEntry on the profile to what it was previously now that we
   568  	// have reverted the individual users' balances.
   569  	existingProfileEntry.CoinEntry = *operationData.PrevCoinEntry
   570  	bav._setProfileEntryMappings(existingProfileEntry)
   571  
   572  	// Now revert the basic transfer with the remaining operations. Cut off
   573  	// the CreatorCoin operation at the end since we just reverted it.
   574  	return bav._disconnectBasicTransfer(
   575  		currentTxn, txnHash, utxoOpsForTxn[:operationIndex+1], blockHeight)
   576  }
   577  
   578  func (bav *UtxoView) _disconnectCreatorCoinTransfer(
   579  	operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash,
   580  	utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error {
   581  
   582  	// Verify that the last operation is a CreatorCoinTransfer operation
   583  	if len(utxoOpsForTxn) == 0 {
   584  		return fmt.Errorf("_disconnectCreatorCoinTransfer: utxoOperations are missing")
   585  	}
   586  	operationIndex := len(utxoOpsForTxn) - 1
   587  	if utxoOpsForTxn[operationIndex].Type != OperationTypeCreatorCoinTransfer {
   588  		return fmt.Errorf("_disconnectCreatorCoinTransfer: Trying to revert "+
   589  			"OperationTypeCreatorCoinTransfer but found type %v",
   590  			utxoOpsForTxn[operationIndex].Type)
   591  	}
   592  	txMeta := currentTxn.TxnMeta.(*CreatorCoinTransferMetadataa)
   593  	operationData := utxoOpsForTxn[operationIndex]
   594  	operationIndex--
   595  
   596  	// Get the profile corresponding to the creator coin txn.
   597  	existingProfileEntry := bav.GetProfileEntryForPublicKey(txMeta.ProfilePublicKey)
   598  	// Sanity-check that it exists.
   599  	if existingProfileEntry == nil || existingProfileEntry.isDeleted {
   600  		return fmt.Errorf("_disconnectCreatorCoinTransfer: CreatorCoinTransfer profile for "+
   601  			"public key %v doesn't exist; this should never happen",
   602  			PkToStringBoth(txMeta.ProfilePublicKey))
   603  	}
   604  
   605  	// Get the current / previous balance for the sender for sanity checking.
   606  	senderBalanceEntry, _, _ := bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
   607  		currentTxn.PublicKey, txMeta.ProfilePublicKey)
   608  	// Sanity-check that the sender had a previous BalanceEntry, it should always exist.
   609  	if operationData.PrevSenderBalanceEntry == nil || operationData.PrevSenderBalanceEntry.isDeleted {
   610  		return fmt.Errorf("_disconnectCreatorCoinTransfer: Previous sender BalanceEntry "+
   611  			"pubkey %v and creator pubkey %v does not exist; this should "+
   612  			"never happen",
   613  			PkToStringBoth(currentTxn.PublicKey), PkToStringBoth(txMeta.ProfilePublicKey))
   614  	}
   615  	senderPrevBalanceNanos := operationData.PrevSenderBalanceEntry.BalanceNanos
   616  	var senderCurrBalanceNanos uint64
   617  	// Since the sender may have given away their whole balance, their BalanceEntry can be nil.
   618  	if senderBalanceEntry != nil && !senderBalanceEntry.isDeleted {
   619  		senderCurrBalanceNanos = senderBalanceEntry.BalanceNanos
   620  	}
   621  
   622  	// Get the current / previous balance for the receiver for sanity checking.
   623  	receiverBalanceEntry, _, _ := bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
   624  		txMeta.ReceiverPublicKey, txMeta.ProfilePublicKey)
   625  	// Sanity-check that the receiver BalanceEntry exists, it should always exist here.
   626  	if receiverBalanceEntry == nil || receiverBalanceEntry.isDeleted {
   627  		return fmt.Errorf("_disconnectCreatorCoinTransfer: Receiver BalanceEntry "+
   628  			"pubkey %v and creator pubkey %v does not exist; this should "+
   629  			"never happen",
   630  			PkToStringBoth(currentTxn.PublicKey), PkToStringBoth(txMeta.ProfilePublicKey))
   631  	}
   632  	receiverCurrBalanceNanos := receiverBalanceEntry.BalanceNanos
   633  	var receiverPrevBalanceNanos uint64
   634  	if operationData.PrevReceiverBalanceEntry != nil {
   635  		receiverPrevBalanceNanos = operationData.PrevReceiverBalanceEntry.BalanceNanos
   636  	}
   637  
   638  	// Sanity check that the sender's current balance is less than their previous balance.
   639  	if senderCurrBalanceNanos > senderPrevBalanceNanos {
   640  		return fmt.Errorf("_disconnectCreatorCoinTransfer: Sender's current balance %d is "+
   641  			"greater than their previous balance %d.",
   642  			senderCurrBalanceNanos, senderPrevBalanceNanos)
   643  	}
   644  
   645  	// Sanity check that the receiver's previous balance is less than their current balance.
   646  	if receiverPrevBalanceNanos > receiverCurrBalanceNanos {
   647  		return fmt.Errorf("_disconnectCreatorCoinTransfer: Receiver's previous balance %d is "+
   648  			"greater than their current balance %d.",
   649  			receiverPrevBalanceNanos, receiverCurrBalanceNanos)
   650  	}
   651  
   652  	// Sanity check the sender's increase equals the receiver's decrease after disconnect.
   653  	senderBalanceIncrease := senderPrevBalanceNanos - senderCurrBalanceNanos
   654  	receiverBalanceDecrease := receiverCurrBalanceNanos - receiverPrevBalanceNanos
   655  	if senderBalanceIncrease != receiverBalanceDecrease {
   656  		return fmt.Errorf("_disconnectCreatorCoinTransfer: Sender's balance increase "+
   657  			"of %d will not equal the receiver's balance decrease of  %v after disconnect.",
   658  			senderBalanceIncrease, receiverBalanceDecrease)
   659  	}
   660  
   661  	// At this point we have sanity checked the current and previous state. Now we just
   662  	// need to revert the mappings.
   663  
   664  	// Delete the sender/receiver balance entries (they will be added back later if needed).
   665  	bav._deleteBalanceEntryMappings(
   666  		receiverBalanceEntry, txMeta.ReceiverPublicKey, txMeta.ProfilePublicKey)
   667  	if senderBalanceEntry != nil {
   668  		bav._deleteBalanceEntryMappings(
   669  			senderBalanceEntry, currentTxn.PublicKey, txMeta.ProfilePublicKey)
   670  	}
   671  
   672  	// Set the balance entries appropriately.
   673  	bav._setBalanceEntryMappings(operationData.PrevSenderBalanceEntry)
   674  	if operationData.PrevReceiverBalanceEntry != nil && operationData.PrevReceiverBalanceEntry.BalanceNanos != 0 {
   675  		bav._setBalanceEntryMappings(operationData.PrevReceiverBalanceEntry)
   676  	}
   677  
   678  	// Reset the CoinEntry on the profile to what it was previously now that we
   679  	// have reverted the individual users' balances.
   680  	existingProfileEntry.CoinEntry = *operationData.PrevCoinEntry
   681  	bav._setProfileEntryMappings(existingProfileEntry)
   682  
   683  	// If the transaction had diamonds, let's revert those too.
   684  	diamondPostHashBytes, hasDiamondPostHash := currentTxn.ExtraData[DiamondPostHashKey]
   685  	if hasDiamondPostHash {
   686  		// Sanity check the post hash bytes before creating the post hash.
   687  		diamondPostHash := &BlockHash{}
   688  		if len(diamondPostHashBytes) != HashSizeBytes {
   689  			return fmt.Errorf(
   690  				"_disconnectCreatorCoin: DiamondPostHashBytes has incorrect length: %d",
   691  				len(diamondPostHashBytes))
   692  		}
   693  		copy(diamondPostHash[:], diamondPostHashBytes[:])
   694  
   695  		// Get the existing diamondEntry so we can delete it.
   696  		senderPKID := bav.GetPKIDForPublicKey(currentTxn.PublicKey)
   697  		receiverPKID := bav.GetPKIDForPublicKey(txMeta.ReceiverPublicKey)
   698  		diamondKey := MakeDiamondKey(senderPKID.PKID, receiverPKID.PKID, diamondPostHash)
   699  		diamondEntry := bav.GetDiamondEntryForDiamondKey(&diamondKey)
   700  
   701  		// Sanity check that the diamondEntry is not nil.
   702  		if diamondEntry == nil {
   703  			return fmt.Errorf(
   704  				"_disconnectCreatorCoin: Found nil diamond entry for diamondKey: %v", &diamondKey)
   705  		}
   706  
   707  		// Delete the diamond entry mapping and re-add it if the previous mapping is not nil.
   708  		bav._deleteDiamondEntryMappings(diamondEntry)
   709  		if operationData.PrevDiamondEntry != nil {
   710  			bav._setDiamondEntryMappings(operationData.PrevDiamondEntry)
   711  		}
   712  
   713  		// Finally, revert the post entry mapping since we likely updated the DiamondCount.
   714  		bav._setPostEntryMappings(operationData.PrevPostEntry)
   715  	}
   716  
   717  	// Now revert the basic transfer with the remaining operations. Cut off
   718  	// the CreatorCoin operation at the end since we just reverted it.
   719  	return bav._disconnectBasicTransfer(
   720  		currentTxn, txnHash, utxoOpsForTxn[:operationIndex+1], blockHeight)
   721  }
   722  
   723  // TODO: A lot of duplicate code between buy and sell. Consider factoring
   724  // out the common code.
   725  func (bav *UtxoView) HelpConnectCreatorCoinBuy(
   726  	txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) (
   727  	_totalInput uint64, _totalOutput uint64, _creatorCoinReturnedNanos uint64, _founderRewardNanos uint64,
   728  	_utxoOps []*UtxoOperation, _err error) {
   729  
   730  	// Connect basic txn to get the total input and the total output without
   731  	// considering the transaction metadata.
   732  	totalInput, totalOutput, utxoOpsForTxn, err := bav._connectBasicTransfer(
   733  		txn, txHash, blockHeight, verifySignatures)
   734  	if err != nil {
   735  		return 0, 0, 0, 0, nil, errors.Wrapf(err, "_connectCreatorCoin: ")
   736  	}
   737  
   738  	// Force the input to be non-zero so that we can prevent replay attacks. If
   739  	// we didn't do this then someone could replay your sell over and over again
   740  	// to force-convert all your creator coin into DeSo. Think about it.
   741  	if totalInput == 0 {
   742  		return 0, 0, 0, 0, nil, RuleErrorCreatorCoinRequiresNonZeroInput
   743  	}
   744  
   745  	// At this point the inputs and outputs have been processed. Now we
   746  	// need to handle the metadata.
   747  
   748  	// Check that the specified profile public key is valid and that a profile
   749  	// corresponding to that public key exists.
   750  	txMeta := txn.TxnMeta.(*CreatorCoinMetadataa)
   751  	if len(txMeta.ProfilePublicKey) != btcec.PubKeyBytesLenCompressed {
   752  		return 0, 0, 0, 0, nil, RuleErrorCreatorCoinInvalidPubKeySize
   753  	}
   754  
   755  	// Dig up the profile. It must exist for the user to be able to
   756  	// operate on its coin.
   757  	existingProfileEntry := bav.GetProfileEntryForPublicKey(txMeta.ProfilePublicKey)
   758  	if existingProfileEntry == nil || existingProfileEntry.isDeleted {
   759  		return 0, 0, 0, 0, nil, errors.Wrapf(
   760  			RuleErrorCreatorCoinOperationOnNonexistentProfile,
   761  			"_connectCreatorCoin: Profile pub key: %v %v",
   762  			PkToStringMainnet(txMeta.ProfilePublicKey), PkToStringTestnet(txMeta.ProfilePublicKey))
   763  	}
   764  
   765  	// At this point we are confident that we have a profile that
   766  	// exists that corresponds to the profile public key the user
   767  	// provided.
   768  
   769  	// Check that the amount of DeSo being traded for creator coin is
   770  	// non-zero.
   771  	desoBeforeFeesNanos := txMeta.DeSoToSellNanos
   772  	if desoBeforeFeesNanos == 0 {
   773  		return 0, 0, 0, 0, nil, RuleErrorCreatorCoinBuyMustTradeNonZeroDeSo
   774  	}
   775  	// The amount of DeSo being traded counts as output being spent by
   776  	// this transaction, so add it to the transaction output and check that
   777  	// the resulting output does not exceed the total input.
   778  	//
   779  	// Check for overflow of the outputs before adding.
   780  	if totalOutput > math.MaxUint64-desoBeforeFeesNanos {
   781  		return 0, 0, 0, 0, nil, errors.Wrapf(
   782  			RuleErrorCreatorCoinTxnOutputWithInvalidBuyAmount,
   783  			"_connectCreatorCoin: %v", desoBeforeFeesNanos)
   784  	}
   785  	totalOutput += desoBeforeFeesNanos
   786  	// It's assumed the caller code will check that things like output <= input,
   787  	// but we check it here just in case...
   788  	if totalInput < totalOutput {
   789  		return 0, 0, 0, 0, nil, errors.Wrapf(
   790  			RuleErrorCreatorCoinTxnOutputExceedsInput,
   791  			"_connectCreatorCoin: Input: %v, Output: %v", totalInput, totalOutput)
   792  	}
   793  	// At this point we have verified that the output is sufficient to cover
   794  	// the amount the user wants to use to buy the creator's coin.
   795  
   796  	// Now we burn some DeSo before executing the creator coin buy. Doing
   797  	// this guarantees that floating point errors in our subsequent calculations
   798  	// will not result in a user being able to print infinite amounts of DeSo
   799  	// through the protocol.
   800  	//
   801  	// TODO(performance): We use bigints to avoid overflow in the intermediate
   802  	// stages of the calculation but this most likely isn't necessary. This
   803  	// formula is equal to:
   804  	// - desoAfterFeesNanos = desoBeforeFeesNanos * (CreatorCoinTradeFeeBasisPoints / (100*100))
   805  	desoAfterFeesNanos := IntDiv(
   806  		IntMul(
   807  			big.NewInt(int64(desoBeforeFeesNanos)),
   808  			big.NewInt(int64(100*100-bav.Params.CreatorCoinTradeFeeBasisPoints))),
   809  		big.NewInt(100*100)).Uint64()
   810  
   811  	// The amount of DeSo being convertend must be nonzero after fees as well.
   812  	if desoAfterFeesNanos == 0 {
   813  		return 0, 0, 0, 0, nil, RuleErrorCreatorCoinBuyMustTradeNonZeroDeSoAfterFees
   814  	}
   815  
   816  	// Figure out how much deso goes to the founder.
   817  	// Note: If the user performing this transaction has the same public key as the
   818  	// profile being bought, we do not cut a founder reward.
   819  	desoRemainingNanos := uint64(0)
   820  	desoFounderRewardNanos := uint64(0)
   821  	if blockHeight > DeSoFounderRewardBlockHeight &&
   822  		!reflect.DeepEqual(txn.PublicKey, existingProfileEntry.PublicKey) {
   823  
   824  		// This formula is equal to:
   825  		// desoFounderRewardNanos = desoAfterFeesNanos * creatorBasisPoints / (100*100)
   826  		desoFounderRewardNanos = IntDiv(
   827  			IntMul(
   828  				big.NewInt(int64(desoAfterFeesNanos)),
   829  				big.NewInt(int64(existingProfileEntry.CreatorBasisPoints))),
   830  			big.NewInt(100*100)).Uint64()
   831  
   832  		// Sanity check, just to be extra safe.
   833  		if desoAfterFeesNanos < desoFounderRewardNanos {
   834  			return 0, 0, 0, 0, nil, fmt.Errorf("HelpConnectCreatorCoinBuy: desoAfterFeesNanos"+
   835  				" less than desoFounderRewardNanos: %v %v",
   836  				desoAfterFeesNanos, desoFounderRewardNanos)
   837  		}
   838  
   839  		desoRemainingNanos = desoAfterFeesNanos - desoFounderRewardNanos
   840  	} else {
   841  		desoRemainingNanos = desoAfterFeesNanos
   842  	}
   843  
   844  	if desoRemainingNanos == 0 {
   845  		return 0, 0, 0, 0, nil, RuleErrorCreatorCoinBuyMustTradeNonZeroDeSoAfterFounderReward
   846  	}
   847  
   848  	// If no DeSo is currently locked in the profile then we use the
   849  	// polynomial equation to mint creator coins. We do this because the
   850  	// Uniswap/Bancor equations don't work when zero coins have been minted,
   851  	// and so we have to special case here. See this wolfram sheet for all
   852  	// the equations with tests:
   853  	// - https://pastebin.com/raw/1EmgeW56
   854  	//
   855  	// Note also that we use big floats with a custom math library in order
   856  	// to guarantee that all nodes get the same result regardless of what
   857  	// architecture they're running on. If we didn't do this, then some nodes
   858  	// could round floats or use different levels of precision for intermediate
   859  	// results and get different answers which would break consensus.
   860  	creatorCoinToMintNanos := CalculateCreatorCoinToMint(
   861  		desoRemainingNanos, existingProfileEntry.CoinsInCirculationNanos,
   862  		existingProfileEntry.DeSoLockedNanos, bav.Params)
   863  
   864  	// Check if the total amount minted satisfies CreatorCoinAutoSellThresholdNanos.
   865  	// This makes it prohibitively expensive for a user to buy themself above the
   866  	// CreatorCoinAutoSellThresholdNanos and then spam tiny nano DeSo creator
   867  	// coin purchases causing the effective Bancor Creator Coin Reserve Ratio to drift.
   868  	if blockHeight > SalomonFixBlockHeight {
   869  		if creatorCoinToMintNanos < bav.Params.CreatorCoinAutoSellThresholdNanos {
   870  			return 0, 0, 0, 0, nil, RuleErrorCreatorCoinBuyMustSatisfyAutoSellThresholdNanos
   871  		}
   872  	}
   873  
   874  	// At this point, we know how much creator coin we are going to mint.
   875  	// Now it's just a matter of adjusting our bookkeeping and potentially
   876  	// giving the creator a founder reward.
   877  
   878  	// Save all the old values from the CoinEntry before we potentially
   879  	// update them. Note that CoinEntry doesn't contain any pointers and so
   880  	// a direct copy is OK.
   881  	prevCoinEntry := existingProfileEntry.CoinEntry
   882  
   883  	// Increment DeSoLockedNanos. Sanity-check that we're not going to
   884  	// overflow.
   885  	if existingProfileEntry.DeSoLockedNanos > math.MaxUint64-desoRemainingNanos {
   886  		return 0, 0, 0, 0, nil, fmt.Errorf("_connectCreatorCoin: Overflow while summing"+
   887  			"DeSoLockedNanos and desoAfterFounderRewardNanos: %v %v",
   888  			existingProfileEntry.DeSoLockedNanos, desoRemainingNanos)
   889  	}
   890  	existingProfileEntry.DeSoLockedNanos += desoRemainingNanos
   891  
   892  	// Increment CoinsInCirculation. Sanity-check that we're not going to
   893  	// overflow.
   894  	if existingProfileEntry.CoinsInCirculationNanos > math.MaxUint64-creatorCoinToMintNanos {
   895  		return 0, 0, 0, 0, nil, fmt.Errorf("_connectCreatorCoin: Overflow while summing"+
   896  			"CoinsInCirculationNanos and creatorCoinToMintNanos: %v %v",
   897  			existingProfileEntry.CoinsInCirculationNanos, creatorCoinToMintNanos)
   898  	}
   899  	existingProfileEntry.CoinsInCirculationNanos += creatorCoinToMintNanos
   900  
   901  	// Calculate the *Creator Coin nanos* to give as a founder reward.
   902  	creatorCoinFounderRewardNanos := uint64(0)
   903  	if blockHeight > DeSoFounderRewardBlockHeight {
   904  		// Do nothing. The chain stopped minting creator coins as a founder reward for
   905  		// creators at this blockheight.  It gives DeSo as a founder reward now instead.
   906  
   907  	} else if blockHeight > SalomonFixBlockHeight {
   908  		// Following the SalomonFixBlockHeight block, creator coin buys continuously mint
   909  		// a founders reward based on the CreatorBasisPoints.
   910  
   911  		creatorCoinFounderRewardNanos = IntDiv(
   912  			IntMul(
   913  				big.NewInt(int64(creatorCoinToMintNanos)),
   914  				big.NewInt(int64(existingProfileEntry.CreatorBasisPoints))),
   915  			big.NewInt(100*100)).Uint64()
   916  	} else {
   917  		// Up to and including the SalomonFixBlockHeight block, creator coin buys only minted
   918  		// a founders reward if the creator reached a new all time high.
   919  
   920  		if existingProfileEntry.CoinsInCirculationNanos > existingProfileEntry.CoinWatermarkNanos {
   921  			// This value must be positive if we made it past the if condition above.
   922  			watermarkDiff := existingProfileEntry.CoinsInCirculationNanos - existingProfileEntry.CoinWatermarkNanos
   923  			// The founder reward is computed as a percentage of the "net coins created,"
   924  			// which is equal to the watermarkDiff
   925  			creatorCoinFounderRewardNanos = IntDiv(
   926  				IntMul(
   927  					big.NewInt(int64(watermarkDiff)),
   928  					big.NewInt(int64(existingProfileEntry.CreatorBasisPoints))),
   929  				big.NewInt(100*100)).Uint64()
   930  		}
   931  	}
   932  
   933  	// CoinWatermarkNanos is no longer used, however it may be helpful for
   934  	// future analytics or updates so we continue to update it here.
   935  	if existingProfileEntry.CoinsInCirculationNanos > existingProfileEntry.CoinWatermarkNanos {
   936  		existingProfileEntry.CoinWatermarkNanos = existingProfileEntry.CoinsInCirculationNanos
   937  	}
   938  
   939  	// At this point, founderRewardNanos will be non-zero if and only if we increased
   940  	// the watermark *and* there was a non-zero CreatorBasisPoints set on the CoinEntry
   941  	// *and* the blockHeight is less than DeSoFounderRewardBlockHeight.
   942  
   943  	// The user gets whatever's left after we pay the founder their reward.
   944  	coinsBuyerGetsNanos := creatorCoinToMintNanos - creatorCoinFounderRewardNanos
   945  
   946  	// If the coins the buyer is getting is less than the minimum threshold that
   947  	// they expected to get, then the transaction is invalid. This prevents
   948  	// front-running attacks, but it also prevents the buyer from getting a
   949  	// terrible price.
   950  	//
   951  	// Note that when the min is set to zero it means we should skip this check.
   952  	if txMeta.MinCreatorCoinExpectedNanos != 0 &&
   953  		coinsBuyerGetsNanos < txMeta.MinCreatorCoinExpectedNanos {
   954  		return 0, 0, 0, 0, nil, errors.Wrapf(
   955  			RuleErrorCreatorCoinLessThanMinimumSetByUser,
   956  			"_connectCreatorCoin: Amount that would be minted and given to user: "+
   957  				"%v, amount that would be given to founder: %v, amount user needed: %v",
   958  			coinsBuyerGetsNanos, creatorCoinFounderRewardNanos, txMeta.MinCreatorCoinExpectedNanos)
   959  	}
   960  
   961  	// If we get here, we are good to go. We will now update the balance of the
   962  	// buyer and the creator (assuming we had a non-zero founderRewardNanos).
   963  
   964  	// Look up a CreatorCoinBalanceEntry for the buyer and the creator. Create
   965  	// an entry for each if one doesn't exist already.
   966  	buyerBalanceEntry, hodlerPKID, creatorPKID :=
   967  		bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
   968  			txn.PublicKey, existingProfileEntry.PublicKey)
   969  	// If the user does not have a balance entry or the user's balance entry is deleted and we have passed the
   970  	// BuyCreatorCoinAfterDeletedBalanceEntryFixBlockHeight, we create a new balance entry.
   971  	if buyerBalanceEntry == nil ||
   972  		(buyerBalanceEntry.isDeleted && blockHeight > BuyCreatorCoinAfterDeletedBalanceEntryFixBlockHeight) {
   973  		// If there is no balance entry for this mapping yet then just create it.
   974  		// In this case the balance will be zero.
   975  		buyerBalanceEntry = &BalanceEntry{
   976  			// The person who created the txn is they buyer/hodler
   977  			HODLerPKID: hodlerPKID,
   978  			// The creator is the owner of the profile that corresponds to the coin.
   979  			CreatorPKID:  creatorPKID,
   980  			BalanceNanos: uint64(0),
   981  		}
   982  	}
   983  
   984  	// Get the balance entry for the creator. In this case the creator owns
   985  	// their own coin and therefore the creator is also the HODLer. We need
   986  	// this so we can pay the creator their founder reward. Note that we have
   987  	// a special case when the creator is purchasing their own coin.
   988  	var creatorBalanceEntry *BalanceEntry
   989  	if reflect.DeepEqual(txn.PublicKey, existingProfileEntry.PublicKey) {
   990  		// If the creator is buying their own coin, don't fetch/create a
   991  		// duplicate entry. If we didn't do this, we might wind up with two
   992  		// duplicate BalanceEntrys when a creator is buying their own coin.
   993  		creatorBalanceEntry = buyerBalanceEntry
   994  	} else {
   995  		// In this case, the creator is distinct from the buyer, so fetch and
   996  		// potentially create a new BalanceEntry for them rather than using the
   997  		// existing one.
   998  		creatorBalanceEntry, hodlerPKID, creatorPKID = bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
   999  			existingProfileEntry.PublicKey, existingProfileEntry.PublicKey)
  1000  		// If the creator does not have a balance entry or the creator's balance entry is deleted and we have passed the
  1001  		// BuyCreatorCoinAfterDeletedBalanceEntryFixBlockHeight, we create a new balance entry.
  1002  		if creatorBalanceEntry == nil ||
  1003  			(creatorBalanceEntry.isDeleted && blockHeight > BuyCreatorCoinAfterDeletedBalanceEntryFixBlockHeight) {
  1004  			// If there is no balance entry then it means the creator doesn't own
  1005  			// any of their coin yet. In this case we create a new entry for them
  1006  			// with a zero balance.
  1007  			creatorBalanceEntry = &BalanceEntry{
  1008  				HODLerPKID:   hodlerPKID,
  1009  				CreatorPKID:  creatorPKID,
  1010  				BalanceNanos: uint64(0),
  1011  			}
  1012  		}
  1013  	}
  1014  	// At this point we should have a BalanceEntry for the buyer and the creator.
  1015  	// These may be the same BalancEntry if the creator is buying their own coin,
  1016  	// but that is OK.
  1017  
  1018  	// Save the previous balance entry before modifying it. If the creator is
  1019  	// buying their own coin, this will be the same BalanceEntry, which is fine.
  1020  	prevBuyerBalanceEntry := *buyerBalanceEntry
  1021  	prevCreatorBalanceEntry := *creatorBalanceEntry
  1022  
  1023  	// Increase the buyer and the creator's balances by the amounts computed
  1024  	// previously. Always check for overflow.
  1025  	if buyerBalanceEntry.BalanceNanos > math.MaxUint64-coinsBuyerGetsNanos {
  1026  		return 0, 0, 0, 0, nil, fmt.Errorf("_connectCreatorCoin: Overflow while summing"+
  1027  			"buyerBalanceEntry.BalanceNanos and coinsBuyerGetsNanos %v %v",
  1028  			buyerBalanceEntry.BalanceNanos, coinsBuyerGetsNanos)
  1029  	}
  1030  	// Check that if the buyer is receiving nanos for the first time, it's enough
  1031  	// to push them above the CreatorCoinAutoSellThresholdNanos threshold. This helps
  1032  	// prevent tiny amounts of nanos from drifting the ratio of creator coins to DeSo locked.
  1033  	if blockHeight > SalomonFixBlockHeight {
  1034  		if buyerBalanceEntry.BalanceNanos == 0 && coinsBuyerGetsNanos != 0 &&
  1035  			coinsBuyerGetsNanos < bav.Params.CreatorCoinAutoSellThresholdNanos {
  1036  			return 0, 0, 0, 0, nil, RuleErrorCreatorCoinBuyMustSatisfyAutoSellThresholdNanosForBuyer
  1037  		}
  1038  	}
  1039  
  1040  	// Check if this is the buyers first buy or first buy after a complete sell.
  1041  	// If it is, we increment the NumberOfHolders to reflect this value.
  1042  	if buyerBalanceEntry.BalanceNanos == 0 && coinsBuyerGetsNanos != 0 {
  1043  		// Increment number of holders by one to reflect the buyer
  1044  		existingProfileEntry.NumberOfHolders += 1
  1045  
  1046  		// Update the profile to reflect the new number of holders
  1047  		bav._setProfileEntryMappings(existingProfileEntry)
  1048  	}
  1049  	// Finally increment the buyerBalanceEntry.BalanceNanos to reflect
  1050  	// the purchased coinsBuyerGetsNanos. If coinsBuyerGetsNanos is greater than 0, we set HasPurchased to true.
  1051  	buyerBalanceEntry.BalanceNanos += coinsBuyerGetsNanos
  1052  	buyerBalanceEntry.HasPurchased = true
  1053  
  1054  	// If the creator is buying their own coin, this will just be modifying
  1055  	// the same pointer as the buyerBalanceEntry, which is what we want.
  1056  	if creatorBalanceEntry.BalanceNanos > math.MaxUint64-creatorCoinFounderRewardNanos {
  1057  		return 0, 0, 0, 0, nil, fmt.Errorf("_connectCreatorCoin: Overflow while summing"+
  1058  			"creatorBalanceEntry.BalanceNanos and creatorCoinFounderRewardNanos %v %v",
  1059  			creatorBalanceEntry.BalanceNanos, creatorCoinFounderRewardNanos)
  1060  	}
  1061  	// Check that if the creator is receiving nanos for the first time, it's enough
  1062  	// to push them above the CreatorCoinAutoSellThresholdNanos threshold. This helps
  1063  	// prevent tiny amounts of nanos from drifting the effective creator coin reserve ratio drift.
  1064  	if creatorBalanceEntry.BalanceNanos == 0 &&
  1065  		creatorCoinFounderRewardNanos != 0 &&
  1066  		creatorCoinFounderRewardNanos < bav.Params.CreatorCoinAutoSellThresholdNanos &&
  1067  		blockHeight > SalomonFixBlockHeight {
  1068  
  1069  		return 0, 0, 0, 0, nil, RuleErrorCreatorCoinBuyMustSatisfyAutoSellThresholdNanosForCreator
  1070  	}
  1071  	// Check if the creator's balance is going from zero to non-zero and increment the NumberOfHolders if so.
  1072  	if creatorBalanceEntry.BalanceNanos == 0 && creatorCoinFounderRewardNanos != 0 {
  1073  		// Increment number of holders by one to reflect the creator
  1074  		existingProfileEntry.NumberOfHolders += 1
  1075  
  1076  		// Update the profile to reflect the new number of holders
  1077  		bav._setProfileEntryMappings(existingProfileEntry)
  1078  	}
  1079  	creatorBalanceEntry.BalanceNanos += creatorCoinFounderRewardNanos
  1080  
  1081  	// At this point the balances for the buyer and the creator should be correct
  1082  	// so set the mappings in the view.
  1083  	bav._setBalanceEntryMappings(buyerBalanceEntry)
  1084  	// Avoid setting the same entry twice if the creator is buying their own coin.
  1085  	if buyerBalanceEntry != creatorBalanceEntry {
  1086  		bav._setBalanceEntryMappings(creatorBalanceEntry)
  1087  	}
  1088  
  1089  	// Finally, if the creator is getting a deso founder reward, add a UTXO for it.
  1090  	var outputKey *UtxoKey
  1091  	if blockHeight > DeSoFounderRewardBlockHeight {
  1092  		if desoFounderRewardNanos > 0 {
  1093  			// Create a new entry for this output and add it to the view. It should be
  1094  			// added at the end of the utxo list.
  1095  			outputKey = &UtxoKey{
  1096  				TxID: *txHash,
  1097  				// The output is like an extra virtual output at the end of the transaction.
  1098  				Index: uint32(len(txn.TxOutputs)),
  1099  			}
  1100  
  1101  			utxoEntry := UtxoEntry{
  1102  				AmountNanos: desoFounderRewardNanos,
  1103  				PublicKey:   existingProfileEntry.PublicKey,
  1104  				BlockHeight: blockHeight,
  1105  				UtxoType:    UtxoTypeCreatorCoinFounderReward,
  1106  				UtxoKey:     outputKey,
  1107  				// We leave the position unset and isSpent to false by default.
  1108  				// The position will be set in the call to _addUtxo.
  1109  			}
  1110  
  1111  			utxoOp, err := bav._addUtxo(&utxoEntry)
  1112  			if err != nil {
  1113  				return 0, 0, 0, 0, nil, errors.Wrapf(err, "HelpConnectCreatorCoinBuy: Problem adding output utxo")
  1114  			}
  1115  
  1116  			// Rosetta uses this UtxoOperation to provide INPUT amounts
  1117  			utxoOpsForTxn = append(utxoOpsForTxn, utxoOp)
  1118  		}
  1119  	}
  1120  
  1121  	// Compute the change in DESO locked. This information is needed by Rosetta
  1122  	// and it's much more efficient to compute it here than it is to recompute
  1123  	// it later.
  1124  	if existingProfileEntry == nil || existingProfileEntry.isDeleted {
  1125  		return 0, 0, 0, 0, nil, errors.Wrapf(err, "HelpConnectCreatorCoinBuy: Error computing "+
  1126  			"desoLockedNanosDiff: Missing profile")
  1127  	}
  1128  	desoLockedNanosDiff := int64(existingProfileEntry.DeSoLockedNanos) - int64(prevCoinEntry.DeSoLockedNanos)
  1129  
  1130  	// Add an operation to the list at the end indicating we've executed a
  1131  	// CreatorCoin txn. Save the previous state of the CoinEntry for easy
  1132  	// reversion during disconnect.
  1133  	utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{
  1134  		Type:                           OperationTypeCreatorCoin,
  1135  		PrevCoinEntry:                  &prevCoinEntry,
  1136  		PrevTransactorBalanceEntry:     &prevBuyerBalanceEntry,
  1137  		PrevCreatorBalanceEntry:        &prevCreatorBalanceEntry,
  1138  		FounderRewardUtxoKey:           outputKey,
  1139  		CreatorCoinDESOLockedNanosDiff: desoLockedNanosDiff,
  1140  	})
  1141  
  1142  	return totalInput, totalOutput, coinsBuyerGetsNanos, creatorCoinFounderRewardNanos, utxoOpsForTxn, nil
  1143  }
  1144  
  1145  // TODO: A lot of duplicate code between buy and sell. Consider factoring
  1146  // out the common code.
  1147  func (bav *UtxoView) HelpConnectCreatorCoinSell(
  1148  	txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) (
  1149  	_totalInput uint64, _totalOutput uint64, _desoReturnedNanos uint64,
  1150  	_utxoOps []*UtxoOperation, _err error) {
  1151  
  1152  	// Connect basic txn to get the total input and the total output without
  1153  	// considering the transaction metadata.
  1154  	totalInput, totalOutput, utxoOpsForTxn, err := bav._connectBasicTransfer(
  1155  		txn, txHash, blockHeight, verifySignatures)
  1156  	if err != nil {
  1157  		return 0, 0, 0, nil, errors.Wrapf(err, "_connectCreatorCoin: ")
  1158  	}
  1159  
  1160  	// Force the input to be non-zero so that we can prevent replay attacks. If
  1161  	// we didn't do this then someone could replay your sell over and over again
  1162  	// to force-convert all your creator coin into DeSo. Think about it.
  1163  	if totalInput == 0 {
  1164  		return 0, 0, 0, nil, RuleErrorCreatorCoinRequiresNonZeroInput
  1165  	}
  1166  
  1167  	// Verify that the output does not exceed the input. This check should also
  1168  	// be done by the caller, but we do it here as well.
  1169  	if totalInput < totalOutput {
  1170  		return 0, 0, 0, nil, errors.Wrapf(
  1171  			RuleErrorCreatorCoinTxnOutputExceedsInput,
  1172  			"_connectCreatorCoin: Input: %v, Output: %v", totalInput, totalOutput)
  1173  	}
  1174  
  1175  	// At this point the inputs and outputs have been processed. Now we
  1176  	// need to handle the metadata.
  1177  
  1178  	// Check that the specified profile public key is valid and that a profile
  1179  	// corresponding to that public key exists.
  1180  	txMeta := txn.TxnMeta.(*CreatorCoinMetadataa)
  1181  	if len(txMeta.ProfilePublicKey) != btcec.PubKeyBytesLenCompressed {
  1182  		return 0, 0, 0, nil, RuleErrorCreatorCoinInvalidPubKeySize
  1183  	}
  1184  
  1185  	// Dig up the profile. It must exist for the user to be able to
  1186  	// operate on its coin.
  1187  	existingProfileEntry := bav.GetProfileEntryForPublicKey(txMeta.ProfilePublicKey)
  1188  	if existingProfileEntry == nil || existingProfileEntry.isDeleted {
  1189  		return 0, 0, 0, nil, errors.Wrapf(
  1190  			RuleErrorCreatorCoinOperationOnNonexistentProfile,
  1191  			"_connectCreatorCoin: Profile pub key: %v %v",
  1192  			PkToStringMainnet(txMeta.ProfilePublicKey), PkToStringTestnet(txMeta.ProfilePublicKey))
  1193  	}
  1194  
  1195  	// At this point we are confident that we have a profile that
  1196  	// exists that corresponds to the profile public key the user
  1197  	// provided.
  1198  
  1199  	// Look up a BalanceEntry for the seller. If it doesn't exist then the seller
  1200  	// implicitly has a balance of zero coins, and so the sell transaction shouldn't be
  1201  	// allowed.
  1202  	sellerBalanceEntry, _, _ := bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
  1203  		txn.PublicKey, existingProfileEntry.PublicKey)
  1204  	if sellerBalanceEntry == nil || sellerBalanceEntry.isDeleted {
  1205  		return 0, 0, 0, nil, RuleErrorCreatorCoinSellerBalanceEntryDoesNotExist
  1206  	}
  1207  
  1208  	// Check that the amount of creator coin being sold is non-zero.
  1209  	creatorCoinToSellNanos := txMeta.CreatorCoinToSellNanos
  1210  	if creatorCoinToSellNanos == 0 {
  1211  		return 0, 0, 0, nil, RuleErrorCreatorCoinSellMustTradeNonZeroCreatorCoin
  1212  	}
  1213  
  1214  	// Check that the amount of creator coin being sold does not exceed the user's
  1215  	// balance of this particular creator coin.
  1216  	if creatorCoinToSellNanos > sellerBalanceEntry.BalanceNanos {
  1217  		return 0, 0, 0, nil, errors.Wrapf(
  1218  			RuleErrorCreatorCoinSellInsufficientCoins,
  1219  			"_connectCreatorCoin: CreatorCoin nanos being sold %v exceeds "+
  1220  				"user's creator coin balance %v",
  1221  			creatorCoinToSellNanos, sellerBalanceEntry.BalanceNanos)
  1222  	}
  1223  
  1224  	// If the amount of DeSo locked in the profile is zero then selling is
  1225  	// not allowed.
  1226  	if existingProfileEntry.DeSoLockedNanos == 0 {
  1227  		return 0, 0, 0, nil, RuleErrorCreatorCoinSellNotAllowedWhenZeroDeSoLocked
  1228  	}
  1229  
  1230  	desoBeforeFeesNanos := uint64(0)
  1231  	// Compute the amount of DeSo to return.
  1232  	if blockHeight > SalomonFixBlockHeight {
  1233  		// Following the SalomonFixBlockHeight block, if a user would be left with less than
  1234  		// bav.Params.CreatorCoinAutoSellThresholdNanos, we clear all their remaining holdings
  1235  		// to prevent 1 or 2 lingering creator coin nanos from staying in their wallet.
  1236  		// This also gives a method for cleanly and accurately reducing the numberOfHolders.
  1237  
  1238  		// Note that we check that sellerBalanceEntry.BalanceNanos >= creatorCoinToSellNanos above.
  1239  		if sellerBalanceEntry.BalanceNanos-creatorCoinToSellNanos < bav.Params.CreatorCoinAutoSellThresholdNanos {
  1240  			// Setup to sell all the creator coins the seller has.
  1241  			creatorCoinToSellNanos = sellerBalanceEntry.BalanceNanos
  1242  
  1243  			// Compute the amount of DeSo to return with the new creatorCoinToSellNanos.
  1244  			desoBeforeFeesNanos = CalculateDeSoToReturn(
  1245  				creatorCoinToSellNanos, existingProfileEntry.CoinsInCirculationNanos,
  1246  				existingProfileEntry.DeSoLockedNanos, bav.Params)
  1247  
  1248  			// If the amount the formula is offering is more than what is locked in the
  1249  			// profile, then truncate it down. This addresses an edge case where our
  1250  			// equations may return *too much* DeSo due to rounding errors.
  1251  			if desoBeforeFeesNanos > existingProfileEntry.DeSoLockedNanos {
  1252  				desoBeforeFeesNanos = existingProfileEntry.DeSoLockedNanos
  1253  			}
  1254  		} else {
  1255  			// If we're above the CreatorCoinAutoSellThresholdNanos, we can safely compute
  1256  			// the amount to return based on the Bancor curve.
  1257  			desoBeforeFeesNanos = CalculateDeSoToReturn(
  1258  				creatorCoinToSellNanos, existingProfileEntry.CoinsInCirculationNanos,
  1259  				existingProfileEntry.DeSoLockedNanos, bav.Params)
  1260  
  1261  			// If the amount the formula is offering is more than what is locked in the
  1262  			// profile, then truncate it down. This addresses an edge case where our
  1263  			// equations may return *too much* DeSo due to rounding errors.
  1264  			if desoBeforeFeesNanos > existingProfileEntry.DeSoLockedNanos {
  1265  				desoBeforeFeesNanos = existingProfileEntry.DeSoLockedNanos
  1266  			}
  1267  		}
  1268  	} else {
  1269  		// Prior to the SalomonFixBlockHeight block, coins would be minted based on floating point
  1270  		// arithmetic with the exception being if a creator was selling all remaining creator coins. This caused
  1271  		// a rare issue where a creator would be left with 1 creator coin nano in circulation
  1272  		// and 1 nano DeSo locked after completely selling. This in turn made the Bancor Curve unstable.
  1273  
  1274  		if creatorCoinToSellNanos == existingProfileEntry.CoinsInCirculationNanos {
  1275  			desoBeforeFeesNanos = existingProfileEntry.DeSoLockedNanos
  1276  		} else {
  1277  			// Calculate the amount to return based on the Bancor Curve.
  1278  			desoBeforeFeesNanos = CalculateDeSoToReturn(
  1279  				creatorCoinToSellNanos, existingProfileEntry.CoinsInCirculationNanos,
  1280  				existingProfileEntry.DeSoLockedNanos, bav.Params)
  1281  
  1282  			// If the amount the formula is offering is more than what is locked in the
  1283  			// profile, then truncate it down. This addresses an edge case where our
  1284  			// equations may return *too much* DeSo due to rounding errors.
  1285  			if desoBeforeFeesNanos > existingProfileEntry.DeSoLockedNanos {
  1286  				desoBeforeFeesNanos = existingProfileEntry.DeSoLockedNanos
  1287  			}
  1288  		}
  1289  	}
  1290  
  1291  	// Save all the old values from the CoinEntry before we potentially
  1292  	// update them. Note that CoinEntry doesn't contain any pointers and so
  1293  	// a direct copy is OK.
  1294  	prevCoinEntry := existingProfileEntry.CoinEntry
  1295  
  1296  	// Subtract the amount of DeSo the seller is getting from the amount of
  1297  	// DeSo locked in the profile. Sanity-check that it does not exceed the
  1298  	// total amount of DeSo locked.
  1299  	if desoBeforeFeesNanos > existingProfileEntry.DeSoLockedNanos {
  1300  		return 0, 0, 0, nil, fmt.Errorf("_connectCreatorCoin: DeSo nanos seller "+
  1301  			"would get %v exceeds DeSo nanos locked in profile %v",
  1302  			desoBeforeFeesNanos, existingProfileEntry.DeSoLockedNanos)
  1303  	}
  1304  	existingProfileEntry.DeSoLockedNanos -= desoBeforeFeesNanos
  1305  
  1306  	// Subtract the number of coins the seller is selling from the number of coins
  1307  	// in circulation. Sanity-check that it does not exceed the number of coins
  1308  	// currently in circulation.
  1309  	if creatorCoinToSellNanos > existingProfileEntry.CoinsInCirculationNanos {
  1310  		return 0, 0, 0, nil, fmt.Errorf("_connectCreatorCoin: CreatorCoin nanos seller "+
  1311  			"is selling %v exceeds CreatorCoin nanos in circulation %v",
  1312  			creatorCoinToSellNanos, existingProfileEntry.CoinsInCirculationNanos)
  1313  	}
  1314  	existingProfileEntry.CoinsInCirculationNanos -= creatorCoinToSellNanos
  1315  
  1316  	// Check if this is a complete sell of the seller's remaining creator coins
  1317  	if sellerBalanceEntry.BalanceNanos == creatorCoinToSellNanos {
  1318  		existingProfileEntry.NumberOfHolders -= 1
  1319  	}
  1320  
  1321  	// If the number of holders has reached zero, we clear all the DeSoLockedNanos and
  1322  	// creatorCoinToSellNanos to ensure that the profile is reset to its normal initial state.
  1323  	// It's okay to modify these values because they are saved in the PrevCoinEntry.
  1324  	if existingProfileEntry.NumberOfHolders == 0 {
  1325  		existingProfileEntry.DeSoLockedNanos = 0
  1326  		existingProfileEntry.CoinsInCirculationNanos = 0
  1327  	}
  1328  
  1329  	// Save the seller's balance before we modify it. We don't need to save the
  1330  	// creator's BalancEntry on a sell because the creator's balance will not
  1331  	// be modified.
  1332  	prevTransactorBalanceEntry := *sellerBalanceEntry
  1333  
  1334  	// Subtract the number of coins the seller is selling from the number of coins
  1335  	// they HODL. Note that we already checked that this amount does not exceed the
  1336  	// seller's balance above. Note that this amount equals sellerBalanceEntry.BalanceNanos
  1337  	// in the event where the requested remaining creator coin balance dips
  1338  	// below CreatorCoinAutoSellThresholdNanos.
  1339  	sellerBalanceEntry.BalanceNanos -= creatorCoinToSellNanos
  1340  
  1341  	// If the seller's balance will be zero after this transaction, set HasPurchased to false
  1342  	if sellerBalanceEntry.BalanceNanos == 0 {
  1343  		sellerBalanceEntry.HasPurchased = false
  1344  	}
  1345  
  1346  	// Set the new BalanceEntry in our mappings for the seller and set the
  1347  	// ProfileEntry mappings as well since everything is up to date.
  1348  	bav._setBalanceEntryMappings(sellerBalanceEntry)
  1349  	bav._setProfileEntryMappings(existingProfileEntry)
  1350  
  1351  	// Charge a fee on the DeSo the seller is getting to hedge against
  1352  	// floating point errors
  1353  	desoAfterFeesNanos := IntDiv(
  1354  		IntMul(
  1355  			big.NewInt(int64(desoBeforeFeesNanos)),
  1356  			big.NewInt(int64(100*100-bav.Params.CreatorCoinTradeFeeBasisPoints))),
  1357  		big.NewInt(100*100)).Uint64()
  1358  
  1359  	// Check that the seller is getting back an amount of DeSo that is
  1360  	// greater than or equal to what they expect. Note that this check is
  1361  	// skipped if the min amount specified is zero.
  1362  	if txMeta.MinDeSoExpectedNanos != 0 &&
  1363  		desoAfterFeesNanos < txMeta.MinDeSoExpectedNanos {
  1364  
  1365  		return 0, 0, 0, nil, errors.Wrapf(
  1366  			RuleErrorDeSoReceivedIsLessThanMinimumSetBySeller,
  1367  			"_connectCreatorCoin: DeSo nanos that would be given to seller: "+
  1368  				"%v, amount user needed: %v",
  1369  			desoAfterFeesNanos, txMeta.MinDeSoExpectedNanos)
  1370  	}
  1371  
  1372  	// Now that we have all the information we need, save a UTXO allowing the user to
  1373  	// spend the DeSo from the sale in the future.
  1374  	outputKey := UtxoKey{
  1375  		TxID: *txn.Hash(),
  1376  		// The output is like an extra virtual output at the end of the transaction.
  1377  		Index: uint32(len(txn.TxOutputs)),
  1378  	}
  1379  	utxoEntry := UtxoEntry{
  1380  		AmountNanos: desoAfterFeesNanos,
  1381  		PublicKey:   txn.PublicKey,
  1382  		BlockHeight: blockHeight,
  1383  		UtxoType:    UtxoTypeCreatorCoinSale,
  1384  		UtxoKey:     &outputKey,
  1385  		// We leave the position unset and isSpent to false by default.
  1386  		// The position will be set in the call to _addUtxo.
  1387  	}
  1388  	// If we have a problem adding this utxo return an error but don't
  1389  	// mark this block as invalid since it's not a rule error and the block
  1390  	// could therefore benefit from being processed in the future.
  1391  	utxoOp, err := bav._addUtxo(&utxoEntry)
  1392  	if err != nil {
  1393  		return 0, 0, 0, nil, errors.Wrapf(
  1394  			err, "_connectBitcoinExchange: Problem adding output utxo")
  1395  	}
  1396  
  1397  	// Rosetta uses this UtxoOperation to provide INPUT amounts
  1398  	utxoOpsForTxn = append(utxoOpsForTxn, utxoOp)
  1399  
  1400  	// Compute the change in DESO locked. This information is needed by Rosetta
  1401  	// and it's much more efficient to compute it here than it is to recompute
  1402  	// it later.
  1403  	if existingProfileEntry == nil || existingProfileEntry.isDeleted {
  1404  		return 0, 0, 0, nil, errors.Wrapf(
  1405  			err, "HelpConnectCreatorCoinSell: Error computing "+
  1406  				"desoLockedNanosDiff: Missing profile")
  1407  	}
  1408  	desoLockedNanosDiff := int64(existingProfileEntry.DeSoLockedNanos) - int64(prevCoinEntry.DeSoLockedNanos)
  1409  
  1410  	// Add an operation to the list at the end indicating we've executed a
  1411  	// CreatorCoin txn. Save the previous state of the CoinEntry for easy
  1412  	// reversion during disconnect.
  1413  	utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{
  1414  		Type:                           OperationTypeCreatorCoin,
  1415  		PrevCoinEntry:                  &prevCoinEntry,
  1416  		PrevTransactorBalanceEntry:     &prevTransactorBalanceEntry,
  1417  		PrevCreatorBalanceEntry:        nil,
  1418  		CreatorCoinDESOLockedNanosDiff: desoLockedNanosDiff,
  1419  	})
  1420  
  1421  	// The DeSo that the user gets from selling their creator coin counts
  1422  	// as both input and output in the transaction.
  1423  	return totalInput + desoAfterFeesNanos,
  1424  		totalOutput + desoAfterFeesNanos,
  1425  		desoAfterFeesNanos, utxoOpsForTxn, nil
  1426  }
  1427  
  1428  func (bav *UtxoView) _connectCreatorCoin(
  1429  	txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) (
  1430  	_totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) {
  1431  
  1432  	// Check that the transaction has the right TxnType.
  1433  	if txn.TxnMeta.GetTxnType() != TxnTypeCreatorCoin {
  1434  		return 0, 0, nil, fmt.Errorf("_connectCreatorCoin: called with bad TxnType %s",
  1435  			txn.TxnMeta.GetTxnType().String())
  1436  	}
  1437  	txMeta := txn.TxnMeta.(*CreatorCoinMetadataa)
  1438  
  1439  	// We save the previous CoinEntry so that we can revert things easily during a
  1440  	// disconnect. If we didn't do this, it would be annoying to reset the coin
  1441  	// state when reverting a transaction.
  1442  	switch txMeta.OperationType {
  1443  	case CreatorCoinOperationTypeBuy:
  1444  		// We don't need the creatorCoinsReturned return value
  1445  		totalInput, totalOutput, _, _, utxoOps, err :=
  1446  			bav.HelpConnectCreatorCoinBuy(txn, txHash, blockHeight, verifySignatures)
  1447  		return totalInput, totalOutput, utxoOps, err
  1448  
  1449  	case CreatorCoinOperationTypeSell:
  1450  		// We don't need the desoReturned return value
  1451  		totalInput, totalOutput, _, utxoOps, err :=
  1452  			bav.HelpConnectCreatorCoinSell(txn, txHash, blockHeight, verifySignatures)
  1453  		return totalInput, totalOutput, utxoOps, err
  1454  
  1455  	case CreatorCoinOperationTypeAddDeSo:
  1456  		return 0, 0, nil, fmt.Errorf("_connectCreatorCoin: Add DeSo not implemented")
  1457  	}
  1458  
  1459  	return 0, 0, nil, fmt.Errorf("_connectCreatorCoin: Unrecognized CreatorCoin "+
  1460  		"OperationType: %v", txMeta.OperationType)
  1461  }
  1462  
  1463  func (bav *UtxoView) _connectCreatorCoinTransfer(
  1464  	txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) (
  1465  	_totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) {
  1466  
  1467  	// Check that the transaction has the right TxnType.
  1468  	if txn.TxnMeta.GetTxnType() != TxnTypeCreatorCoinTransfer {
  1469  		return 0, 0, nil, fmt.Errorf("_connectCreatorCoinTransfer: called with bad TxnType %s",
  1470  			txn.TxnMeta.GetTxnType().String())
  1471  	}
  1472  	txMeta := txn.TxnMeta.(*CreatorCoinTransferMetadataa)
  1473  
  1474  	// Connect basic txn to get the total input and the total output without
  1475  	// considering the transaction metadata.
  1476  	totalInput, totalOutput, utxoOpsForTxn, err := bav._connectBasicTransfer(
  1477  		txn, txHash, blockHeight, verifySignatures)
  1478  	if err != nil {
  1479  		return 0, 0, nil, errors.Wrapf(err, "_connectCreatorCoin: ")
  1480  	}
  1481  
  1482  	// Force the input to be non-zero so that we can prevent replay attacks. If
  1483  	// we didn't do this then someone could replay your transfer over and over again
  1484  	// to force-convert all your creator coin into DeSo. Think about it.
  1485  	if totalInput == 0 {
  1486  		return 0, 0, nil, RuleErrorCreatorCoinTransferRequiresNonZeroInput
  1487  	}
  1488  
  1489  	// At this point the inputs and outputs have been processed. Now we
  1490  	// need to handle the metadata.
  1491  
  1492  	// Check that the specified receiver public key is valid.
  1493  	if len(txMeta.ReceiverPublicKey) != btcec.PubKeyBytesLenCompressed {
  1494  		return 0, 0, nil, RuleErrorCreatorCoinTransferInvalidReceiverPubKeySize
  1495  	}
  1496  
  1497  	// Check that the sender and receiver public keys are different.
  1498  	if reflect.DeepEqual(txn.PublicKey, txMeta.ReceiverPublicKey) {
  1499  		return 0, 0, nil, RuleErrorCreatorCoinTransferCannotTransferToSelf
  1500  	}
  1501  
  1502  	// Check that the specified profile public key is valid and that a profile
  1503  	// corresponding to that public key exists.
  1504  	if len(txMeta.ProfilePublicKey) != btcec.PubKeyBytesLenCompressed {
  1505  		return 0, 0, nil, RuleErrorCreatorCoinTransferInvalidProfilePubKeySize
  1506  	}
  1507  
  1508  	// Dig up the profile. It must exist for the user to be able to transfer its coin.
  1509  	existingProfileEntry := bav.GetProfileEntryForPublicKey(txMeta.ProfilePublicKey)
  1510  	if existingProfileEntry == nil || existingProfileEntry.isDeleted {
  1511  		return 0, 0, nil, errors.Wrapf(
  1512  			RuleErrorCreatorCoinTransferOnNonexistentProfile,
  1513  			"_connectCreatorCoin: Profile pub key: %v %v",
  1514  			PkToStringMainnet(txMeta.ProfilePublicKey), PkToStringTestnet(txMeta.ProfilePublicKey))
  1515  	}
  1516  
  1517  	// At this point we are confident that we have a profile that
  1518  	// exists that corresponds to the profile public key the user provided.
  1519  
  1520  	// Look up a BalanceEntry for the sender. If it doesn't exist then the sender implicitly
  1521  	// has a balance of zero coins, and so the transfer shouldn't be allowed.
  1522  	senderBalanceEntry, _, _ := bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
  1523  		txn.PublicKey, existingProfileEntry.PublicKey)
  1524  	if senderBalanceEntry == nil || senderBalanceEntry.isDeleted {
  1525  		return 0, 0, nil, RuleErrorCreatorCoinTransferBalanceEntryDoesNotExist
  1526  	}
  1527  
  1528  	// Check that the amount of creator coin being transferred is not less than the min threshold.
  1529  	if txMeta.CreatorCoinToTransferNanos < bav.Params.CreatorCoinAutoSellThresholdNanos {
  1530  		return 0, 0, nil, RuleErrorCreatorCoinTransferMustBeGreaterThanMinThreshold
  1531  	}
  1532  
  1533  	// Check that the amount of creator coin being transferred does not exceed the user's
  1534  	// balance of this particular creator coin.
  1535  	if txMeta.CreatorCoinToTransferNanos > senderBalanceEntry.BalanceNanos {
  1536  		return 0, 0, nil, errors.Wrapf(
  1537  			RuleErrorCreatorCoinTransferInsufficientCoins,
  1538  			"_connectCreatorCoin: CreatorCoin nanos being transferred %v exceeds "+
  1539  				"user's creator coin balance %v",
  1540  			txMeta.CreatorCoinToTransferNanos, senderBalanceEntry.BalanceNanos)
  1541  	}
  1542  
  1543  	// Now that we have validated this transaction, let's build the new BalanceEntry state.
  1544  
  1545  	// Look up a BalanceEntry for the receiver.
  1546  	receiverBalanceEntry, _, _ := bav.GetBalanceEntryForHODLerPubKeyAndCreatorPubKey(
  1547  		txMeta.ReceiverPublicKey, txMeta.ProfilePublicKey)
  1548  
  1549  	// Save the receiver's balance if it is non-nil.
  1550  	var prevReceiverBalanceEntry *BalanceEntry
  1551  	if receiverBalanceEntry != nil {
  1552  		prevReceiverBalanceEntry = &BalanceEntry{}
  1553  		*prevReceiverBalanceEntry = *receiverBalanceEntry
  1554  	}
  1555  
  1556  	// If the receiver's balance entry is nil, we need to make one.
  1557  	if receiverBalanceEntry == nil || receiverBalanceEntry.isDeleted {
  1558  		receiverPKID := bav.GetPKIDForPublicKey(txMeta.ReceiverPublicKey)
  1559  		creatorPKID := bav.GetPKIDForPublicKey(existingProfileEntry.PublicKey)
  1560  		// Sanity check that we found a PKID entry for these pub keys (should never fail).
  1561  		if receiverPKID == nil || receiverPKID.isDeleted || creatorPKID == nil || creatorPKID.isDeleted {
  1562  			return 0, 0, nil, fmt.Errorf(
  1563  				"_connectCreatorCoin: Found nil or deleted PKID for receiver or creator, this should never "+
  1564  					"happen. Receiver pubkey: %v, creator pubkey: %v",
  1565  				PkToStringMainnet(txMeta.ReceiverPublicKey),
  1566  				PkToStringMainnet(existingProfileEntry.PublicKey))
  1567  		}
  1568  		receiverBalanceEntry = &BalanceEntry{
  1569  			HODLerPKID:   receiverPKID.PKID,
  1570  			CreatorPKID:  creatorPKID.PKID,
  1571  			BalanceNanos: uint64(0),
  1572  		}
  1573  	}
  1574  
  1575  	// Save the sender's balance before we modify it.
  1576  	prevSenderBalanceEntry := *senderBalanceEntry
  1577  
  1578  	// Subtract the number of coins being given from the sender and add them to the receiver.
  1579  	// TODO: We should avoid editing the pointer returned by "bav._getX" directly before
  1580  	// deleting / setting. Since the pointer returned is the one held by the view, it
  1581  	// makes setting redundant.  An alternative would be to not call _set after modification.
  1582  	senderBalanceEntry.BalanceNanos -= txMeta.CreatorCoinToTransferNanos
  1583  	receiverBalanceEntry.BalanceNanos += txMeta.CreatorCoinToTransferNanos
  1584  
  1585  	// We do not allow accounts to maintain tiny creator coin balances in order to avoid
  1586  	// Bancor curve price anomalies as famously demonstrated by @salomon.  Thus, if the
  1587  	// sender tries to make a transfer that will leave them below the threshold we give
  1588  	// their remaining balance to the receiver in order to zero them out.
  1589  	if senderBalanceEntry.BalanceNanos < bav.Params.CreatorCoinAutoSellThresholdNanos {
  1590  		receiverBalanceEntry.BalanceNanos += senderBalanceEntry.BalanceNanos
  1591  		senderBalanceEntry.BalanceNanos = 0
  1592  		senderBalanceEntry.HasPurchased = false
  1593  	}
  1594  
  1595  	// Delete the sender's balance entry under the assumption that the sender gave away all
  1596  	// of their coins. We add it back later, if this is not the case.
  1597  	bav._deleteBalanceEntryMappings(senderBalanceEntry, txn.PublicKey, txMeta.ProfilePublicKey)
  1598  	// Delete the receiver's balance entry just to be safe. Added back immediately after.
  1599  	bav._deleteBalanceEntryMappings(
  1600  		receiverBalanceEntry, txMeta.ReceiverPublicKey, txMeta.ProfilePublicKey)
  1601  
  1602  	bav._setBalanceEntryMappings(receiverBalanceEntry)
  1603  	if senderBalanceEntry.BalanceNanos > 0 {
  1604  		bav._setBalanceEntryMappings(senderBalanceEntry)
  1605  	}
  1606  
  1607  	// Save all the old values from the CoinEntry before we potentially update them. Note
  1608  	// that CoinEntry doesn't contain any pointers and so a direct copy is OK.
  1609  	prevCoinEntry := existingProfileEntry.CoinEntry
  1610  
  1611  	if prevReceiverBalanceEntry == nil || prevReceiverBalanceEntry.BalanceNanos == 0 {
  1612  		// The receiver did not have a BalanceEntry before. Increment num holders.
  1613  		existingProfileEntry.CoinEntry.NumberOfHolders++
  1614  	}
  1615  
  1616  	if senderBalanceEntry.BalanceNanos == 0 {
  1617  		// The sender no longer holds any of this creator's coin, so we decrement num holders.
  1618  		existingProfileEntry.CoinEntry.NumberOfHolders--
  1619  	}
  1620  
  1621  	// Update and set the new profile entry.
  1622  	bav._setProfileEntryMappings(existingProfileEntry)
  1623  
  1624  	// If this creator coin transfer has diamonds, validate them and do the connection.
  1625  	diamondPostHashBytes, hasDiamondPostHash := txn.ExtraData[DiamondPostHashKey]
  1626  	diamondPostHash := &BlockHash{}
  1627  	diamondLevelBytes, hasDiamondLevel := txn.ExtraData[DiamondLevelKey]
  1628  	var previousDiamondPostEntry *PostEntry
  1629  	var previousDiamondEntry *DiamondEntry
  1630  	// After the DeSoDiamondsBlockHeight, we no longer accept creator coin diamonds.
  1631  	if hasDiamondPostHash && blockHeight > DeSoDiamondsBlockHeight {
  1632  		return 0, 0, nil, RuleErrorCreatorCoinTransferHasDiamondsAfterDeSoBlockHeight
  1633  	} else if hasDiamondPostHash {
  1634  		if !hasDiamondLevel {
  1635  			return 0, 0, nil, RuleErrorCreatorCoinTransferHasDiamondPostHashWithoutDiamondLevel
  1636  		}
  1637  		diamondLevel, bytesRead := Varint(diamondLevelBytes)
  1638  		// NOTE: Despite being an int, diamondLevel is required to be non-negative. This
  1639  		// is useful for sorting our dbkeys by diamondLevel.
  1640  		if bytesRead < 0 || diamondLevel < 0 {
  1641  			return 0, 0, nil, RuleErrorCreatorCoinTransferHasInvalidDiamondLevel
  1642  		}
  1643  
  1644  		if !reflect.DeepEqual(txn.PublicKey, existingProfileEntry.PublicKey) {
  1645  			return 0, 0, nil, RuleErrorCreatorCoinTransferCantSendDiamondsForOtherProfiles
  1646  		}
  1647  		if reflect.DeepEqual(txMeta.ReceiverPublicKey, existingProfileEntry.PublicKey) {
  1648  			return 0, 0, nil, RuleErrorCreatorCoinTransferCantDiamondYourself
  1649  		}
  1650  
  1651  		if len(diamondPostHashBytes) != HashSizeBytes {
  1652  			return 0, 0, nil, errors.Wrapf(
  1653  				RuleErrorCreatorCoinTransferInvalidLengthForPostHashBytes,
  1654  				"_connectCreatorCoin: DiamondPostHashBytes length: %d", len(diamondPostHashBytes))
  1655  		}
  1656  		copy(diamondPostHash[:], diamondPostHashBytes[:])
  1657  
  1658  		previousDiamondPostEntry = bav.GetPostEntryForPostHash(diamondPostHash)
  1659  		if previousDiamondPostEntry == nil || previousDiamondPostEntry.isDeleted {
  1660  			return 0, 0, nil, RuleErrorCreatorCoinTransferDiamondPostEntryDoesNotExist
  1661  		}
  1662  
  1663  		expectedCreatorCoinNanosToTransfer, netNewDiamonds, err := bav.ValidateDiamondsAndGetNumCreatorCoinNanos(
  1664  			txn.PublicKey, txMeta.ReceiverPublicKey, diamondPostHash, diamondLevel, blockHeight)
  1665  		if err != nil {
  1666  			return 0, 0, nil, errors.Wrapf(err, "_connectCreatorCoin: ")
  1667  		}
  1668  
  1669  		if txMeta.CreatorCoinToTransferNanos < expectedCreatorCoinNanosToTransfer {
  1670  			return 0, 0, nil, RuleErrorCreatorCoinTransferInsufficientCreatorCoinsForDiamondLevel
  1671  		}
  1672  
  1673  		// The diamondPostEntry needs to be updated with the number of new diamonds.
  1674  		// We make a copy to avoid issues with disconnecting.
  1675  		newDiamondPostEntry := &PostEntry{}
  1676  		*newDiamondPostEntry = *previousDiamondPostEntry
  1677  		newDiamondPostEntry.DiamondCount += uint64(netNewDiamonds)
  1678  		bav._setPostEntryMappings(newDiamondPostEntry)
  1679  
  1680  		// Convert pub keys into PKIDs so we can make the DiamondEntry.
  1681  		senderPKID := bav.GetPKIDForPublicKey(txn.PublicKey)
  1682  		receiverPKID := bav.GetPKIDForPublicKey(txMeta.ReceiverPublicKey)
  1683  
  1684  		// Create a new DiamondEntry
  1685  		newDiamondEntry := &DiamondEntry{
  1686  			SenderPKID:      senderPKID.PKID,
  1687  			ReceiverPKID:    receiverPKID.PKID,
  1688  			DiamondPostHash: diamondPostHash,
  1689  			DiamondLevel:    diamondLevel,
  1690  		}
  1691  
  1692  		// Save the old DiamondEntry
  1693  		diamondKey := MakeDiamondKey(senderPKID.PKID, receiverPKID.PKID, diamondPostHash)
  1694  		existingDiamondEntry := bav.GetDiamondEntryForDiamondKey(&diamondKey)
  1695  		// Save the existing DiamondEntry, if it exists, so we can disconnect
  1696  		if existingDiamondEntry != nil {
  1697  			dd := &DiamondEntry{}
  1698  			*dd = *existingDiamondEntry
  1699  			previousDiamondEntry = dd
  1700  		}
  1701  
  1702  		// Now set the diamond entry mappings on the view so they are flushed to the DB.
  1703  		bav._setDiamondEntryMappings(newDiamondEntry)
  1704  	}
  1705  
  1706  	// Add an operation to the list at the end indicating we've executed a
  1707  	// CreatorCoin txn. Save the previous state of the CoinEntry for easy
  1708  	// reversion during disconnect.
  1709  	utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{
  1710  		Type:                     OperationTypeCreatorCoinTransfer,
  1711  		PrevSenderBalanceEntry:   &prevSenderBalanceEntry,
  1712  		PrevReceiverBalanceEntry: prevReceiverBalanceEntry,
  1713  		PrevCoinEntry:            &prevCoinEntry,
  1714  		PrevPostEntry:            previousDiamondPostEntry,
  1715  		PrevDiamondEntry:         previousDiamondEntry,
  1716  	})
  1717  
  1718  	return totalInput, totalOutput, utxoOpsForTxn, nil
  1719  }