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

     1  package lib
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"reflect"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/btcsuite/btcd/btcec"
    11  	"github.com/dgraph-io/badger/v3"
    12  	"github.com/golang/glog"
    13  	"github.com/pkg/errors"
    14  )
    15  
    16  // block_view.go is the main work-horse for validating transactions in blocks.
    17  // It generally works by creating an "in-memory view" of the current tip and
    18  // then applying a transaction's operations to the view to see if those operations
    19  // are allowed and consistent with the blockchain's current state. Generally,
    20  // every transaction we define has a corresponding connect() and disconnect()
    21  // function defined here that specifies what operations that transaction applies
    22  // to the view and ultimately to the database. If you want to know how any
    23  // particular transaction impacts the database, you've found the right file. A
    24  // good place to start in this file is ConnectTransaction and DisconnectTransaction.
    25  // ConnectBlock is also good.
    26  
    27  type UtxoView struct {
    28  	// Utxo data
    29  	NumUtxoEntries              uint64
    30  	UtxoKeyToUtxoEntry          map[UtxoKey]*UtxoEntry
    31  	PublicKeyToDeSoBalanceNanos map[PublicKey]uint64
    32  
    33  	// BitcoinExchange data
    34  	NanosPurchased     uint64
    35  	USDCentsPerBitcoin uint64
    36  	GlobalParamsEntry  *GlobalParamsEntry
    37  	BitcoinBurnTxIDs   map[BlockHash]bool
    38  
    39  	// Forbidden block signature pubkeys
    40  	ForbiddenPubKeyToForbiddenPubKeyEntry map[PkMapKey]*ForbiddenPubKeyEntry
    41  
    42  	// Messages data
    43  	MessageKeyToMessageEntry map[MessageKey]*MessageEntry
    44  
    45  	// Postgres stores message data slightly differently
    46  	MessageMap map[BlockHash]*PGMessage
    47  
    48  	// Follow data
    49  	FollowKeyToFollowEntry map[FollowKey]*FollowEntry
    50  
    51  	// NFT data
    52  	NFTKeyToNFTEntry              map[NFTKey]*NFTEntry
    53  	NFTBidKeyToNFTBidEntry        map[NFTBidKey]*NFTBidEntry
    54  	NFTKeyToAcceptedNFTBidHistory map[NFTKey]*[]*NFTBidEntry
    55  
    56  	// Diamond data
    57  	DiamondKeyToDiamondEntry map[DiamondKey]*DiamondEntry
    58  
    59  	// Like data
    60  	LikeKeyToLikeEntry map[LikeKey]*LikeEntry
    61  
    62  	// Repost data
    63  	RepostKeyToRepostEntry map[RepostKey]*RepostEntry
    64  
    65  	// Post data
    66  	PostHashToPostEntry map[BlockHash]*PostEntry
    67  
    68  	// Profile data
    69  	PublicKeyToPKIDEntry map[PkMapKey]*PKIDEntry
    70  	// The PKIDEntry is only used here to store the public key.
    71  	PKIDToPublicKey               map[PKID]*PKIDEntry
    72  	ProfilePKIDToProfileEntry     map[PKID]*ProfileEntry
    73  	ProfileUsernameToProfileEntry map[UsernameMapKey]*ProfileEntry
    74  
    75  	// Coin balance entries
    76  	HODLerPKIDCreatorPKIDToBalanceEntry map[BalanceEntryMapKey]*BalanceEntry
    77  
    78  	// Derived Key entries. Map key is a combination of owner and derived public keys.
    79  	DerivedKeyToDerivedEntry map[DerivedKeyMapKey]*DerivedKeyEntry
    80  
    81  	// The hash of the tip the view is currently referencing. Mainly used
    82  	// for error-checking when doing a bulk operation on the view.
    83  	TipHash *BlockHash
    84  
    85  	Handle   *badger.DB
    86  	Postgres *Postgres
    87  	Params   *DeSoParams
    88  }
    89  
    90  // Assumes the db Handle is already set on the view, but otherwise the
    91  // initialization is full.
    92  func (bav *UtxoView) _ResetViewMappingsAfterFlush() {
    93  	// Utxo data
    94  	bav.UtxoKeyToUtxoEntry = make(map[UtxoKey]*UtxoEntry)
    95  	// TODO: Deprecate this value
    96  	bav.NumUtxoEntries = GetUtxoNumEntries(bav.Handle)
    97  	bav.PublicKeyToDeSoBalanceNanos = make(map[PublicKey]uint64)
    98  
    99  	// BitcoinExchange data
   100  	bav.NanosPurchased = DbGetNanosPurchased(bav.Handle)
   101  	bav.USDCentsPerBitcoin = DbGetUSDCentsPerBitcoinExchangeRate(bav.Handle)
   102  	bav.GlobalParamsEntry = DbGetGlobalParamsEntry(bav.Handle)
   103  	bav.BitcoinBurnTxIDs = make(map[BlockHash]bool)
   104  
   105  	// Forbidden block signature pub key info.
   106  	bav.ForbiddenPubKeyToForbiddenPubKeyEntry = make(map[PkMapKey]*ForbiddenPubKeyEntry)
   107  
   108  	// Post and profile data
   109  	bav.PostHashToPostEntry = make(map[BlockHash]*PostEntry)
   110  	bav.PublicKeyToPKIDEntry = make(map[PkMapKey]*PKIDEntry)
   111  	bav.PKIDToPublicKey = make(map[PKID]*PKIDEntry)
   112  	bav.ProfilePKIDToProfileEntry = make(map[PKID]*ProfileEntry)
   113  	bav.ProfileUsernameToProfileEntry = make(map[UsernameMapKey]*ProfileEntry)
   114  
   115  	// Messages data
   116  	bav.MessageKeyToMessageEntry = make(map[MessageKey]*MessageEntry)
   117  	bav.MessageMap = make(map[BlockHash]*PGMessage)
   118  
   119  	// Follow data
   120  	bav.FollowKeyToFollowEntry = make(map[FollowKey]*FollowEntry)
   121  
   122  	// NFT data
   123  	bav.NFTKeyToNFTEntry = make(map[NFTKey]*NFTEntry)
   124  	bav.NFTBidKeyToNFTBidEntry = make(map[NFTBidKey]*NFTBidEntry)
   125  	bav.NFTKeyToAcceptedNFTBidHistory = make(map[NFTKey]*[]*NFTBidEntry)
   126  
   127  	// Diamond data
   128  	bav.DiamondKeyToDiamondEntry = make(map[DiamondKey]*DiamondEntry)
   129  
   130  	// Like data
   131  	bav.LikeKeyToLikeEntry = make(map[LikeKey]*LikeEntry)
   132  
   133  	// Repost data
   134  	bav.RepostKeyToRepostEntry = make(map[RepostKey]*RepostEntry)
   135  
   136  	// Coin balance entries
   137  	bav.HODLerPKIDCreatorPKIDToBalanceEntry = make(map[BalanceEntryMapKey]*BalanceEntry)
   138  
   139  	// Derived Key entries
   140  	bav.DerivedKeyToDerivedEntry = make(map[DerivedKeyMapKey]*DerivedKeyEntry)
   141  }
   142  
   143  func (bav *UtxoView) CopyUtxoView() (*UtxoView, error) {
   144  	newView, err := NewUtxoView(bav.Handle, bav.Params, bav.Postgres)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	// Copy the UtxoEntry data
   150  	// Note that using _setUtxoMappings is dangerous because the Pos within
   151  	// the UtxoEntrys is off.
   152  	newView.UtxoKeyToUtxoEntry = make(map[UtxoKey]*UtxoEntry, len(bav.UtxoKeyToUtxoEntry))
   153  	for utxoKey, utxoEntry := range bav.UtxoKeyToUtxoEntry {
   154  		newUtxoEntry := *utxoEntry
   155  		newView.UtxoKeyToUtxoEntry[utxoKey] = &newUtxoEntry
   156  	}
   157  	newView.NumUtxoEntries = bav.NumUtxoEntries
   158  
   159  	// Copy the public key to balance data
   160  	newView.PublicKeyToDeSoBalanceNanos = make(map[PublicKey]uint64, len(bav.PublicKeyToDeSoBalanceNanos))
   161  	for pkMapKey, desoBalance := range bav.PublicKeyToDeSoBalanceNanos {
   162  		newView.PublicKeyToDeSoBalanceNanos[pkMapKey] = desoBalance
   163  	}
   164  
   165  	// Copy the BitcoinExchange data
   166  	newView.BitcoinBurnTxIDs = make(map[BlockHash]bool, len(bav.BitcoinBurnTxIDs))
   167  	for bh := range bav.BitcoinBurnTxIDs {
   168  		newView.BitcoinBurnTxIDs[bh] = true
   169  	}
   170  	newView.NanosPurchased = bav.NanosPurchased
   171  	newView.USDCentsPerBitcoin = bav.USDCentsPerBitcoin
   172  
   173  	// Copy the GlobalParamsEntry
   174  	newGlobalParamsEntry := *bav.GlobalParamsEntry
   175  	newView.GlobalParamsEntry = &newGlobalParamsEntry
   176  
   177  	// Copy the post data
   178  	newView.PostHashToPostEntry = make(map[BlockHash]*PostEntry, len(bav.PostHashToPostEntry))
   179  	for postHash, postEntry := range bav.PostHashToPostEntry {
   180  		if postEntry == nil {
   181  			continue
   182  		}
   183  
   184  		newPostEntry := *postEntry
   185  		newView.PostHashToPostEntry[postHash] = &newPostEntry
   186  	}
   187  
   188  	// Copy the PKID data
   189  	newView.PublicKeyToPKIDEntry = make(map[PkMapKey]*PKIDEntry, len(bav.PublicKeyToPKIDEntry))
   190  	for pkMapKey, pkid := range bav.PublicKeyToPKIDEntry {
   191  		newPKID := *pkid
   192  		newView.PublicKeyToPKIDEntry[pkMapKey] = &newPKID
   193  	}
   194  
   195  	newView.PKIDToPublicKey = make(map[PKID]*PKIDEntry, len(bav.PKIDToPublicKey))
   196  	for pkid, pkidEntry := range bav.PKIDToPublicKey {
   197  		newPKIDEntry := *pkidEntry
   198  		newView.PKIDToPublicKey[pkid] = &newPKIDEntry
   199  	}
   200  
   201  	// Copy the profile data
   202  	newView.ProfilePKIDToProfileEntry = make(map[PKID]*ProfileEntry, len(bav.ProfilePKIDToProfileEntry))
   203  	for profilePKID, profileEntry := range bav.ProfilePKIDToProfileEntry {
   204  		if profileEntry == nil {
   205  			continue
   206  		}
   207  
   208  		newProfileEntry := *profileEntry
   209  		newView.ProfilePKIDToProfileEntry[profilePKID] = &newProfileEntry
   210  	}
   211  	newView.ProfileUsernameToProfileEntry = make(map[UsernameMapKey]*ProfileEntry, len(bav.ProfileUsernameToProfileEntry))
   212  	for profilePKID, profileEntry := range bav.ProfileUsernameToProfileEntry {
   213  		if profileEntry == nil {
   214  			continue
   215  		}
   216  
   217  		newProfileEntry := *profileEntry
   218  		newView.ProfileUsernameToProfileEntry[profilePKID] = &newProfileEntry
   219  	}
   220  
   221  	// Copy the message data
   222  	newView.MessageKeyToMessageEntry = make(map[MessageKey]*MessageEntry, len(bav.MessageKeyToMessageEntry))
   223  	for msgKey, msgEntry := range bav.MessageKeyToMessageEntry {
   224  		newMsgEntry := *msgEntry
   225  		newView.MessageKeyToMessageEntry[msgKey] = &newMsgEntry
   226  	}
   227  
   228  	newView.MessageMap = make(map[BlockHash]*PGMessage, len(bav.MessageMap))
   229  	for txnHash, message := range bav.MessageMap {
   230  		newMessage := *message
   231  		newView.MessageMap[txnHash] = &newMessage
   232  	}
   233  
   234  	// Copy the follow data
   235  	newView.FollowKeyToFollowEntry = make(map[FollowKey]*FollowEntry, len(bav.FollowKeyToFollowEntry))
   236  	for followKey, followEntry := range bav.FollowKeyToFollowEntry {
   237  		if followEntry == nil {
   238  			continue
   239  		}
   240  
   241  		newFollowEntry := *followEntry
   242  		newView.FollowKeyToFollowEntry[followKey] = &newFollowEntry
   243  	}
   244  
   245  	// Copy the like data
   246  	newView.LikeKeyToLikeEntry = make(map[LikeKey]*LikeEntry, len(bav.LikeKeyToLikeEntry))
   247  	for likeKey, likeEntry := range bav.LikeKeyToLikeEntry {
   248  		if likeEntry == nil {
   249  			continue
   250  		}
   251  
   252  		newLikeEntry := *likeEntry
   253  		newView.LikeKeyToLikeEntry[likeKey] = &newLikeEntry
   254  	}
   255  
   256  	// Copy the repost data
   257  	newView.RepostKeyToRepostEntry = make(map[RepostKey]*RepostEntry, len(bav.RepostKeyToRepostEntry))
   258  	for repostKey, repostEntry := range bav.RepostKeyToRepostEntry {
   259  		newRepostEntry := *repostEntry
   260  		newView.RepostKeyToRepostEntry[repostKey] = &newRepostEntry
   261  	}
   262  
   263  	// Copy the balance entry data
   264  	newView.HODLerPKIDCreatorPKIDToBalanceEntry = make(
   265  		map[BalanceEntryMapKey]*BalanceEntry, len(bav.HODLerPKIDCreatorPKIDToBalanceEntry))
   266  	for balanceEntryMapKey, balanceEntry := range bav.HODLerPKIDCreatorPKIDToBalanceEntry {
   267  		if balanceEntry == nil {
   268  			continue
   269  		}
   270  
   271  		newBalanceEntry := *balanceEntry
   272  		newView.HODLerPKIDCreatorPKIDToBalanceEntry[balanceEntryMapKey] = &newBalanceEntry
   273  	}
   274  
   275  	// Copy the Diamond data
   276  	newView.DiamondKeyToDiamondEntry = make(
   277  		map[DiamondKey]*DiamondEntry, len(bav.DiamondKeyToDiamondEntry))
   278  	for diamondKey, diamondEntry := range bav.DiamondKeyToDiamondEntry {
   279  		newDiamondEntry := *diamondEntry
   280  		newView.DiamondKeyToDiamondEntry[diamondKey] = &newDiamondEntry
   281  	}
   282  
   283  	// Copy the NFT data
   284  	newView.NFTKeyToNFTEntry = make(map[NFTKey]*NFTEntry, len(bav.NFTKeyToNFTEntry))
   285  	for nftKey, nftEntry := range bav.NFTKeyToNFTEntry {
   286  		newNFTEntry := *nftEntry
   287  		newView.NFTKeyToNFTEntry[nftKey] = &newNFTEntry
   288  	}
   289  
   290  	newView.NFTBidKeyToNFTBidEntry = make(map[NFTBidKey]*NFTBidEntry, len(bav.NFTBidKeyToNFTBidEntry))
   291  	for nftBidKey, nftBidEntry := range bav.NFTBidKeyToNFTBidEntry {
   292  		newNFTBidEntry := *nftBidEntry
   293  		newView.NFTBidKeyToNFTBidEntry[nftBidKey] = &newNFTBidEntry
   294  	}
   295  
   296  	newView.NFTKeyToAcceptedNFTBidHistory = make(map[NFTKey]*[]*NFTBidEntry, len(bav.NFTKeyToAcceptedNFTBidHistory))
   297  	for nftKey, nftBidEntries := range bav.NFTKeyToAcceptedNFTBidHistory {
   298  		newNFTBidEntries := *nftBidEntries
   299  		newView.NFTKeyToAcceptedNFTBidHistory[nftKey] = &newNFTBidEntries
   300  	}
   301  
   302  	// Copy the Derived Key data
   303  	newView.DerivedKeyToDerivedEntry = make(map[DerivedKeyMapKey]*DerivedKeyEntry, len(bav.DerivedKeyToDerivedEntry))
   304  	for entryKey, entry := range bav.DerivedKeyToDerivedEntry {
   305  		newEntry := *entry
   306  		newView.DerivedKeyToDerivedEntry[entryKey] = &newEntry
   307  	}
   308  
   309  	return newView, nil
   310  }
   311  
   312  func NewUtxoView(
   313  	_handle *badger.DB,
   314  	_params *DeSoParams,
   315  	_postgres *Postgres,
   316  ) (*UtxoView, error) {
   317  
   318  	view := UtxoView{
   319  		Handle: _handle,
   320  		Params: _params,
   321  		// Note that the TipHash does not get reset as part of
   322  		// _ResetViewMappingsAfterFlush because it is not something that is affected by a
   323  		// flush operation. Moreover, its value is consistent with the view regardless of
   324  		// whether or not the view is flushed or not. Additionally the utxo view does
   325  		// not concern itself with the header chain (see comment on GetBestHash for more
   326  		// info on that).
   327  		TipHash: DbGetBestHash(_handle, ChainTypeDeSoBlock /* don't get the header chain */),
   328  
   329  		Postgres: _postgres,
   330  		// Set everything else in _ResetViewMappings()
   331  	}
   332  
   333  	// Note that the TipHash does not get reset as part of
   334  	// _ResetViewMappingsAfterFlush because it is not something that is affected by a
   335  	// flush operation. Moreover, its value is consistent with the view regardless of
   336  	// whether or not the view is flushed or not. Additionally the utxo view does
   337  	// not concern itself with the header chain (see comment on GetBestHash for more
   338  	// info on that).
   339  	if view.Postgres != nil {
   340  		view.TipHash = view.Postgres.GetChain(MAIN_CHAIN).TipHash
   341  	} else {
   342  		view.TipHash = DbGetBestHash(view.Handle, ChainTypeDeSoBlock /* don't get the header chain */)
   343  	}
   344  
   345  	// This function is generally used to reset the view after a flush has been performed
   346  	// but we can use it here to initialize the mappings.
   347  	view._ResetViewMappingsAfterFlush()
   348  
   349  	return &view, nil
   350  }
   351  
   352  func (bav *UtxoView) _deleteUtxoMappings(utxoEntry *UtxoEntry) error {
   353  	if utxoEntry.UtxoKey == nil {
   354  		return fmt.Errorf("_deleteUtxoMappings: utxoKey missing for utxoEntry %+v", utxoEntry)
   355  	}
   356  
   357  	// Deleting a utxo amounts to setting its mappings to point to an
   358  	// entry that has (isSpent = true). So we create such an entry and set
   359  	// the mappings to point to it.
   360  	tombstoneEntry := *utxoEntry
   361  	tombstoneEntry.isSpent = true
   362  
   363  	// _setUtxoMappings will take this and use its fields to update the
   364  	// mappings.
   365  	// TODO: We're doing a double-copy here at the moment. We should make this more
   366  	// efficient.
   367  	return bav._setUtxoMappings(&tombstoneEntry)
   368  
   369  	// Note at this point, the utxoEntry passed in is dangling and can
   370  	// be re-used for another purpose if desired.
   371  }
   372  
   373  func (bav *UtxoView) _setUtxoMappings(utxoEntry *UtxoEntry) error {
   374  	if utxoEntry.UtxoKey == nil {
   375  		return fmt.Errorf("_setUtxoMappings: utxoKey missing for utxoEntry %+v", utxoEntry)
   376  	}
   377  	bav.UtxoKeyToUtxoEntry[*utxoEntry.UtxoKey] = utxoEntry
   378  
   379  	return nil
   380  }
   381  
   382  func (bav *UtxoView) GetUtxoEntryForUtxoKey(utxoKey *UtxoKey) *UtxoEntry {
   383  	utxoEntry, ok := bav.UtxoKeyToUtxoEntry[*utxoKey]
   384  	// If the utxo entry isn't in our in-memory data structure, fetch it from the
   385  	// db.
   386  	if !ok {
   387  		if bav.Postgres != nil {
   388  			utxoEntry = bav.Postgres.GetUtxoEntryForUtxoKey(utxoKey)
   389  		} else {
   390  			utxoEntry = DbGetUtxoEntryForUtxoKey(bav.Handle, utxoKey)
   391  		}
   392  		if utxoEntry == nil {
   393  			// This means the utxo is neither in our map nor in the db so
   394  			// it doesn't exist. Return nil to signal that in this case.
   395  			return nil
   396  		}
   397  
   398  		// At this point we have the utxo entry so load it
   399  		// into our in-memory data structure for future reference. Note that
   400  		// isSpent should be false by default. Also note that a back-reference
   401  		// to the utxoKey should be set on the utxoEntry by this function.
   402  		utxoEntry.UtxoKey = utxoKey
   403  		if err := bav._setUtxoMappings(utxoEntry); err != nil {
   404  			glog.Errorf("GetUtxoEntryForUtxoKey: Problem encountered setting utxo mapping %v", err)
   405  			return nil
   406  		}
   407  	}
   408  
   409  	return utxoEntry
   410  }
   411  
   412  func (bav *UtxoView) GetDeSoBalanceNanosForPublicKey(publicKey []byte) (uint64, error) {
   413  	balanceNanos, hasBalance := bav.PublicKeyToDeSoBalanceNanos[*NewPublicKey(publicKey)]
   414  	if hasBalance {
   415  		return balanceNanos, nil
   416  	}
   417  
   418  	// If the utxo entry isn't in our in-memory data structure, fetch it from the db.
   419  	if bav.Postgres != nil {
   420  		balanceNanos = bav.Postgres.GetBalance(NewPublicKey(publicKey))
   421  	} else {
   422  		var err error
   423  		balanceNanos, err = DbGetDeSoBalanceNanosForPublicKey(bav.Handle, publicKey)
   424  		if err != nil {
   425  			return uint64(0), errors.Wrap(err, "GetDeSoBalanceNanosForPublicKey: ")
   426  		}
   427  	}
   428  
   429  	// Add the balance to memory for future references.
   430  	bav.PublicKeyToDeSoBalanceNanos[*NewPublicKey(publicKey)] = balanceNanos
   431  
   432  	return balanceNanos, nil
   433  }
   434  
   435  func (bav *UtxoView) _unSpendUtxo(utxoEntryy *UtxoEntry) error {
   436  	// Operate on a copy of the entry in order to avoid bugs. Note that not
   437  	// doing this could result in us maintaining a reference to the entry and
   438  	// modifying it on subsequent calls to this function, which is bad.
   439  	utxoEntryCopy := *utxoEntryy
   440  
   441  	// If the utxoKey back-reference on the entry isn't set return an error.
   442  	if utxoEntryCopy.UtxoKey == nil {
   443  		return fmt.Errorf("_unSpendUtxo: utxoEntry must have utxoKey set")
   444  	}
   445  	// Make sure isSpent is set to false. It should be false by default if we
   446  	// read this entry from the db but set it in case the caller derived the
   447  	// entry via a different method.
   448  	utxoEntryCopy.isSpent = false
   449  
   450  	// Not setting this to a copy could cause issues down the road where we modify
   451  	// the utxo passed-in on subsequent calls.
   452  	if err := bav._setUtxoMappings(&utxoEntryCopy); err != nil {
   453  		return err
   454  	}
   455  
   456  	// Since we re-added the utxo, bump the number of entries.
   457  	bav.NumUtxoEntries++
   458  
   459  	// Add the utxo back to the spender's balance.
   460  	desoBalanceNanos, err := bav.GetDeSoBalanceNanosForPublicKey(utxoEntryy.PublicKey)
   461  	if err != nil {
   462  		return errors.Wrap(err, "_unSpendUtxo: ")
   463  	}
   464  	desoBalanceNanos += utxoEntryy.AmountNanos
   465  	bav.PublicKeyToDeSoBalanceNanos[*NewPublicKey(utxoEntryy.PublicKey)] = desoBalanceNanos
   466  
   467  	return nil
   468  }
   469  
   470  func (bav *UtxoView) _spendUtxo(utxoKey *UtxoKey) (*UtxoOperation, error) {
   471  	// Swap this utxo's position with the utxo in the last position and delete it.
   472  
   473  	// Get the entry for this utxo from the view if it's cached,
   474  	// otherwise try and get it from the db.
   475  	utxoEntry := bav.GetUtxoEntryForUtxoKey(utxoKey)
   476  	if utxoEntry == nil {
   477  		return nil, fmt.Errorf("_spendUtxo: Attempting to spend non-existent UTXO")
   478  	}
   479  	if utxoEntry.isSpent {
   480  		return nil, fmt.Errorf("_spendUtxo: Attempting to spend an already-spent UTXO")
   481  	}
   482  
   483  	// Delete the entry by removing its mappings from our in-memory data
   484  	// structures.
   485  	if err := bav._deleteUtxoMappings(utxoEntry); err != nil {
   486  		return nil, errors.Wrapf(err, "_spendUtxo: ")
   487  	}
   488  
   489  	// Decrement the number of entries by one since we marked one as spent in the
   490  	// view.
   491  	bav.NumUtxoEntries--
   492  
   493  	// Deduct the utxo from the spender's balance.
   494  	desoBalanceNanos, err := bav.GetDeSoBalanceNanosForPublicKey(utxoEntry.PublicKey)
   495  	if err != nil {
   496  		return nil, errors.Wrapf(err, "_spendUtxo: ")
   497  	}
   498  	desoBalanceNanos -= utxoEntry.AmountNanos
   499  	bav.PublicKeyToDeSoBalanceNanos[*NewPublicKey(utxoEntry.PublicKey)] = desoBalanceNanos
   500  
   501  	// Record a UtxoOperation in case we want to roll this back in the
   502  	// future. At this point, the UtxoEntry passed in still has all of its
   503  	// fields set to what they were right before SPEND was called. This is
   504  	// exactly what we want (see comment on OperationTypeSpendUtxo for more info).
   505  	// Make a copy of the entry to avoid issues where we accidentally modify
   506  	// the entry in the future.
   507  	utxoEntryCopy := *utxoEntry
   508  	return &UtxoOperation{
   509  		Type:  OperationTypeSpendUtxo,
   510  		Key:   utxoKey,
   511  		Entry: &utxoEntryCopy,
   512  	}, nil
   513  }
   514  
   515  func (bav *UtxoView) _unAddUtxo(utxoKey *UtxoKey) error {
   516  	// Get the entry for this utxo from the view if it's cached,
   517  	// otherwise try and get it from the db.
   518  	utxoEntry := bav.GetUtxoEntryForUtxoKey(utxoKey)
   519  	if utxoEntry == nil {
   520  		return fmt.Errorf("_unAddUtxo: Attempting to remove non-existent UTXO")
   521  	}
   522  	if utxoEntry.isSpent {
   523  		return fmt.Errorf("_unAddUtxo: Attempting to remove an already-spent UTXO")
   524  	}
   525  
   526  	// At this point we should have the entry sanity-checked. To remove
   527  	// it from our data structure, it is sufficient to replace it with an
   528  	// entry that is marked as spent. When the view is eventually flushed
   529  	// to the database the output's status as spent will translate to it
   530  	// getting deleted, which is what we want.
   531  	if err := bav._deleteUtxoMappings(utxoEntry); err != nil {
   532  		return err
   533  	}
   534  
   535  	// In addition to marking the output as spent, we update the number of
   536  	// entries to reflect the output is no longer in our utxo list.
   537  	bav.NumUtxoEntries--
   538  
   539  	// Remove the utxo back from the spender's balance.
   540  	desoBalanceNanos, err := bav.GetDeSoBalanceNanosForPublicKey(utxoEntry.PublicKey)
   541  	if err != nil {
   542  		return errors.Wrapf(err, "_unAddUtxo: ")
   543  	}
   544  	desoBalanceNanos -= utxoEntry.AmountNanos
   545  	bav.PublicKeyToDeSoBalanceNanos[*NewPublicKey(utxoEntry.PublicKey)] = desoBalanceNanos
   546  
   547  	return nil
   548  }
   549  
   550  // Note: We assume that the person passing in the utxo key and the utxo entry
   551  // aren't going to modify them after.
   552  func (bav *UtxoView) _addUtxo(utxoEntryy *UtxoEntry) (*UtxoOperation, error) {
   553  	// Use a copy of the utxo passed in so we avoid keeping a reference to it
   554  	// which could be modified in subsequent calls.
   555  	utxoEntryCopy := *utxoEntryy
   556  
   557  	// If the utxoKey back-reference on the entry isn't set then error.
   558  	if utxoEntryCopy.UtxoKey == nil {
   559  		return nil, fmt.Errorf("_addUtxo: utxoEntry must have utxoKey set")
   560  	}
   561  	// If the UtxoEntry passed in has isSpent set then error. The caller should only
   562  	// pass in entries that are unspent.
   563  	if utxoEntryCopy.isSpent {
   564  		return nil, fmt.Errorf("_addUtxo: UtxoEntry being added has isSpent = true")
   565  	}
   566  
   567  	// Put the utxo at the end and update our in-memory data structures with
   568  	// this change.
   569  	//
   570  	// Note this may over-write an existing entry but this is OK for a very subtle
   571  	// reason. When we roll back a transaction, e.g. due to a
   572  	// reorg, we mark the outputs of that transaction as "spent" but we don't delete them
   573  	// from our view because doing so would cause us to neglect to actually delete them
   574  	// when we flush the view to the db. What this means is that if we roll back a transaction
   575  	// in a block but then add it later in a different block, that second add could
   576  	// over-write the entry that is currently has isSpent=true with a similar (though
   577  	// not identical because the block height may differ) entry that has isSpent=false.
   578  	// This is OK however because the new entry we're over-writing the old entry with
   579  	// has the same key and so flushing the view to the database will result in the
   580  	// deletion of the old entry as intended when the new entry over-writes it. Put
   581  	// simply, the over-write that could happen here is an over-write we also want to
   582  	// happen when we flush and so it should be OK.
   583  	if err := bav._setUtxoMappings(&utxoEntryCopy); err != nil {
   584  		return nil, errors.Wrapf(err, "_addUtxo: ")
   585  	}
   586  
   587  	// Bump the number of entries since we just added this one at the end.
   588  	bav.NumUtxoEntries++
   589  
   590  	// Add the utxo back to the spender's balance.
   591  	desoBalanceNanos, err := bav.GetDeSoBalanceNanosForPublicKey(utxoEntryy.PublicKey)
   592  	if err != nil {
   593  		return nil, errors.Wrapf(err, "_addUtxo: ")
   594  	}
   595  	desoBalanceNanos += utxoEntryy.AmountNanos
   596  	bav.PublicKeyToDeSoBalanceNanos[*NewPublicKey(utxoEntryy.PublicKey)] = desoBalanceNanos
   597  
   598  	// Finally record a UtxoOperation in case we want to roll back this ADD
   599  	// in the future. Note that Entry data isn't required for an ADD operation.
   600  	return &UtxoOperation{
   601  		Type: OperationTypeAddUtxo,
   602  		// We don't technically need these in order to be able to roll back the
   603  		// transaction but they're useful for callers of connectTransaction to
   604  		// determine implicit outputs that were created like those that get created
   605  		// in a Bitcoin burn transaction.
   606  		Key:   utxoEntryCopy.UtxoKey,
   607  		Entry: &utxoEntryCopy,
   608  	}, nil
   609  }
   610  
   611  func (bav *UtxoView) _disconnectBasicTransfer(currentTxn *MsgDeSoTxn, txnHash *BlockHash, utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error {
   612  	// First we check to see if the last utxoOp was a diamond operation. If it was, we disconnect
   613  	// the diamond-related changes and decrement the operation index to move past it.
   614  	operationIndex := len(utxoOpsForTxn) - 1
   615  	if len(utxoOpsForTxn) > 0 && utxoOpsForTxn[operationIndex].Type == OperationTypeDeSoDiamond {
   616  		currentOperation := utxoOpsForTxn[operationIndex]
   617  
   618  		diamondPostHashBytes, hasDiamondPostHash := currentTxn.ExtraData[DiamondPostHashKey]
   619  		if !hasDiamondPostHash {
   620  			return fmt.Errorf("_disconnectBasicTransfer: Found diamond op without diamondPostHash")
   621  		}
   622  
   623  		// Sanity check the post hash bytes before creating the post hash.
   624  		diamondPostHash := &BlockHash{}
   625  		if len(diamondPostHashBytes) != HashSizeBytes {
   626  			return fmt.Errorf(
   627  				"_disconnectBasicTransfer: DiamondPostHashBytes has incorrect length: %d",
   628  				len(diamondPostHashBytes))
   629  		}
   630  		copy(diamondPostHash[:], diamondPostHashBytes[:])
   631  
   632  		// Get the diamonded post entry and make sure it exists.
   633  		diamondedPostEntry := bav.GetPostEntryForPostHash(diamondPostHash)
   634  		if diamondedPostEntry == nil || diamondedPostEntry.isDeleted {
   635  			return fmt.Errorf(
   636  				"_disconnectBasicTransfer: Could not find diamonded post entry: %s",
   637  				diamondPostHash.String())
   638  		}
   639  
   640  		// Get the existing diamondEntry so we can delete it.
   641  		senderPKID := bav.GetPKIDForPublicKey(currentTxn.PublicKey)
   642  		receiverPKID := bav.GetPKIDForPublicKey(diamondedPostEntry.PosterPublicKey)
   643  		diamondKey := MakeDiamondKey(senderPKID.PKID, receiverPKID.PKID, diamondPostHash)
   644  		diamondEntry := bav.GetDiamondEntryForDiamondKey(&diamondKey)
   645  
   646  		// Sanity check that the diamondEntry is not nil.
   647  		if diamondEntry == nil {
   648  			return fmt.Errorf(
   649  				"_disconnectBasicTransfer: Found nil diamond entry for diamondKey: %v", &diamondKey)
   650  		}
   651  
   652  		// Delete the diamond entry mapping and re-add it if the previous mapping is not nil.
   653  		bav._deleteDiamondEntryMappings(diamondEntry)
   654  		if currentOperation.PrevDiamondEntry != nil {
   655  			bav._setDiamondEntryMappings(currentOperation.PrevDiamondEntry)
   656  		}
   657  
   658  		// Finally, revert the post entry mapping since we likely updated the DiamondCount.
   659  		bav._setPostEntryMappings(currentOperation.PrevPostEntry)
   660  
   661  		operationIndex--
   662  	}
   663  
   664  	// Loop through the transaction's outputs backwards and remove them
   665  	// from the view. Since the outputs will have been added to the view
   666  	// at the end of the utxo list, removing them from the view amounts to
   667  	// removing the last element from the utxo list.
   668  	//
   669  	// Loop backwards over the utxo operations as we go along.
   670  	for outputIndex := len(currentTxn.TxOutputs) - 1; outputIndex >= 0; outputIndex-- {
   671  		currentOutput := currentTxn.TxOutputs[outputIndex]
   672  
   673  		// Compute the utxo key for this output so we can reference it in our
   674  		// data structures.
   675  		outputKey := &UtxoKey{
   676  			TxID:  *txnHash,
   677  			Index: uint32(outputIndex),
   678  		}
   679  
   680  		// Verify that the utxo operation we're undoing is an add and advance
   681  		// our index to the next operation.
   682  		currentOperation := utxoOpsForTxn[operationIndex]
   683  		operationIndex--
   684  		if currentOperation.Type != OperationTypeAddUtxo {
   685  			return fmt.Errorf(
   686  				"_disconnectBasicTransfer: Output with key %v does not line up to an "+
   687  					"ADD operation in the passed utxoOps", outputKey)
   688  		}
   689  
   690  		// The current output should be at the end of the utxo list so go
   691  		// ahead and fetch it. Do some sanity checks to make sure the view
   692  		// is in sync with the operations we're trying to perform.
   693  		outputEntry := bav.GetUtxoEntryForUtxoKey(outputKey)
   694  		if outputEntry == nil {
   695  			return fmt.Errorf(
   696  				"_disconnectBasicTransfer: Output with key %v is missing from "+
   697  					"utxo view", outputKey)
   698  		}
   699  		if outputEntry.isSpent {
   700  			return fmt.Errorf(
   701  				"_disconnectBasicTransfer: Output with key %v was spent before "+
   702  					"being removed from the utxo view. This should never "+
   703  					"happen", outputKey)
   704  		}
   705  		if outputEntry.AmountNanos != currentOutput.AmountNanos {
   706  			return fmt.Errorf(
   707  				"_disconnectBasicTransfer: Output with key %v has amount (%d) "+
   708  					"that differs from the amount for the output in the "+
   709  					"view (%d)", outputKey, currentOutput.AmountNanos,
   710  				outputEntry.AmountNanos)
   711  		}
   712  		if !reflect.DeepEqual(outputEntry.PublicKey, currentOutput.PublicKey) {
   713  			return fmt.Errorf(
   714  				"_disconnectBasicTransfer: Output with key %v has public key (%v) "+
   715  					"that differs from the public key for the output in the "+
   716  					"view (%v)", outputKey, currentOutput.PublicKey,
   717  				outputEntry.PublicKey)
   718  		}
   719  		if outputEntry.BlockHeight != blockHeight {
   720  			return fmt.Errorf(
   721  				"_disconnectBasicTransfer: Output with key %v has block height (%d) "+
   722  					"that differs from the block we're disconnecting (%d)",
   723  				outputKey, outputEntry.BlockHeight, blockHeight)
   724  		}
   725  		if outputEntry.UtxoType == UtxoTypeBlockReward && (currentTxn.TxnMeta.GetTxnType() != TxnTypeBlockReward) {
   726  
   727  			return fmt.Errorf(
   728  				"_disconnectBasicTransfer: Output with key %v is a block reward txn according "+
   729  					"to the view, yet is not the first transaction referenced in "+
   730  					"the block", outputKey)
   731  		}
   732  
   733  		if err := bav._unAddUtxo(outputKey); err != nil {
   734  			return errors.Wrapf(err, "_disconnectBasicTransfer: Problem unAdding utxo %v: ", outputKey)
   735  		}
   736  	}
   737  
   738  	// At this point we should have rolled back all of the transaction's outputs
   739  	// in the view. Now we roll back its inputs, similarly processing them in
   740  	// backwards order.
   741  	for inputIndex := len(currentTxn.TxInputs) - 1; inputIndex >= 0; inputIndex-- {
   742  		currentInput := currentTxn.TxInputs[inputIndex]
   743  
   744  		// Convert this input to a utxo key.
   745  		inputKey := UtxoKey(*currentInput)
   746  
   747  		// Get the output entry for this input from the utxoOps that were
   748  		// passed in and check its type. For every input that we're restoring
   749  		// we need a SPEND operation that lines up with it.
   750  		currentOperation := utxoOpsForTxn[operationIndex]
   751  		operationIndex--
   752  		if currentOperation.Type != OperationTypeSpendUtxo {
   753  			return fmt.Errorf(
   754  				"_disconnectBasicTransfer: Input with key %v does not line up with a "+
   755  					"SPEND operation in the passed utxoOps", inputKey)
   756  		}
   757  
   758  		// Check that the input matches the key of the spend we're rolling
   759  		// back.
   760  		if inputKey != *currentOperation.Key {
   761  			return fmt.Errorf(
   762  				"_disconnectBasicTransfer: Input with key %v does not match the key of the "+
   763  					"corresponding SPEND operation in the passed utxoOps %v",
   764  				inputKey, *currentOperation.Key)
   765  		}
   766  
   767  		// Unspend the entry using the information in the UtxoOperation. If the entry
   768  		// was de-serialized from the db it will have its utxoKey unset so we need to
   769  		// set it here in order to make it unspendable.
   770  		currentOperation.Entry.UtxoKey = currentOperation.Key
   771  		if err := bav._unSpendUtxo(currentOperation.Entry); err != nil {
   772  			return errors.Wrapf(err, "_disconnectBasicTransfer: Problem unspending utxo %v: ", currentOperation.Key)
   773  		}
   774  	}
   775  
   776  	return nil
   777  }
   778  
   779  func (bav *UtxoView) _disconnectUpdateGlobalParams(
   780  	operationType OperationType, currentTxn *MsgDeSoTxn, txnHash *BlockHash,
   781  	utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error {
   782  	// Check that the last operation has the required OperationType
   783  	operationIndex := len(utxoOpsForTxn) - 1
   784  	if len(utxoOpsForTxn) == 0 {
   785  		return fmt.Errorf("_disconnectUpdateGlobalParams: Trying to revert "+
   786  			"%v but utxoOperations are missing",
   787  			OperationTypeUpdateGlobalParams)
   788  	}
   789  	if utxoOpsForTxn[operationIndex].Type != OperationTypeUpdateGlobalParams {
   790  		return fmt.Errorf("_disconnectUpdateGlobalParams: Trying to revert "+
   791  			"%v but found type %v",
   792  			OperationTypeUpdateGlobalParams, utxoOpsForTxn[operationIndex].Type)
   793  	}
   794  	operationData := utxoOpsForTxn[operationIndex]
   795  
   796  	// Reset the global params to their previous value.
   797  	// This previous value comes from the UtxoOperation data.
   798  	prevGlobalParamEntry := operationData.PrevGlobalParamsEntry
   799  	if prevGlobalParamEntry == nil {
   800  		prevGlobalParamEntry = &InitialGlobalParamsEntry
   801  	}
   802  	bav.GlobalParamsEntry = prevGlobalParamEntry
   803  
   804  	// Reset any modified forbidden pub key entries if they exist.
   805  	if operationData.PrevForbiddenPubKeyEntry != nil {
   806  		pkMapKey := MakePkMapKey(operationData.PrevForbiddenPubKeyEntry.PubKey)
   807  		bav.ForbiddenPubKeyToForbiddenPubKeyEntry[pkMapKey] = operationData.PrevForbiddenPubKeyEntry
   808  	}
   809  
   810  	// Now revert the basic transfer with the remaining operations. Cut off
   811  	// the UpdateGlobalParams operation at the end since we just reverted it.
   812  	return bav._disconnectBasicTransfer(
   813  		currentTxn, txnHash, utxoOpsForTxn[:operationIndex], blockHeight)
   814  }
   815  
   816  func (bav *UtxoView) DisconnectTransaction(currentTxn *MsgDeSoTxn, txnHash *BlockHash,
   817  	utxoOpsForTxn []*UtxoOperation, blockHeight uint32) error {
   818  
   819  	if currentTxn.TxnMeta.GetTxnType() == TxnTypeBlockReward || currentTxn.TxnMeta.GetTxnType() == TxnTypeBasicTransfer {
   820  		return bav._disconnectBasicTransfer(
   821  			currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   822  
   823  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeBitcoinExchange {
   824  		return bav._disconnectBitcoinExchange(
   825  			OperationTypeBitcoinExchange, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   826  
   827  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypePrivateMessage {
   828  		return bav._disconnectPrivateMessage(
   829  			OperationTypePrivateMessage, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   830  
   831  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeSubmitPost {
   832  		return bav._disconnectSubmitPost(
   833  			OperationTypeSubmitPost, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   834  
   835  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeUpdateProfile {
   836  		return bav._disconnectUpdateProfile(
   837  			OperationTypeUpdateProfile, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   838  
   839  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeUpdateBitcoinUSDExchangeRate {
   840  		return bav._disconnectUpdateBitcoinUSDExchangeRate(
   841  			OperationTypeUpdateBitcoinUSDExchangeRate, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   842  
   843  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeUpdateGlobalParams {
   844  		return bav._disconnectUpdateGlobalParams(
   845  			OperationTypeUpdateGlobalParams, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   846  
   847  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeFollow {
   848  		return bav._disconnectFollow(
   849  			OperationTypeFollow, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   850  
   851  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeLike {
   852  		return bav._disconnectLike(
   853  			OperationTypeFollow, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   854  
   855  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeCreatorCoin {
   856  		return bav._disconnectCreatorCoin(
   857  			OperationTypeCreatorCoin, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   858  
   859  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeCreatorCoinTransfer {
   860  		return bav._disconnectCreatorCoinTransfer(
   861  			OperationTypeCreatorCoinTransfer, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   862  
   863  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeSwapIdentity {
   864  		return bav._disconnectSwapIdentity(
   865  			OperationTypeSwapIdentity, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   866  
   867  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeCreateNFT {
   868  		return bav._disconnectCreateNFT(
   869  			OperationTypeCreateNFT, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   870  
   871  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeUpdateNFT {
   872  		return bav._disconnectUpdateNFT(
   873  			OperationTypeUpdateNFT, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   874  
   875  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeAcceptNFTBid {
   876  		return bav._disconnectAcceptNFTBid(
   877  			OperationTypeAcceptNFTBid, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   878  
   879  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeNFTBid {
   880  		return bav._disconnectNFTBid(
   881  			OperationTypeNFTBid, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   882  
   883  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeNFTTransfer {
   884  		return bav._disconnectNFTTransfer(
   885  			OperationTypeNFTTransfer, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   886  
   887  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeAcceptNFTTransfer {
   888  		return bav._disconnectAcceptNFTTransfer(
   889  			OperationTypeAcceptNFTTransfer, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   890  
   891  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeBurnNFT {
   892  		return bav._disconnectBurnNFT(
   893  			OperationTypeBurnNFT, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   894  
   895  	} else if currentTxn.TxnMeta.GetTxnType() == TxnTypeAuthorizeDerivedKey {
   896  		return bav._disconnectAuthorizeDerivedKey(
   897  			OperationTypeAuthorizeDerivedKey, currentTxn, txnHash, utxoOpsForTxn, blockHeight)
   898  
   899  	}
   900  
   901  	return fmt.Errorf("DisconnectBlock: Unimplemented txn type %v", currentTxn.TxnMeta.GetTxnType().String())
   902  }
   903  
   904  func (bav *UtxoView) DisconnectBlock(
   905  	desoBlock *MsgDeSoBlock, txHashes []*BlockHash, utxoOps [][]*UtxoOperation) error {
   906  
   907  	glog.Infof("DisconnectBlock: Disconnecting block %v", desoBlock)
   908  
   909  	// Verify that the block being disconnected is the current tip. DisconnectBlock
   910  	// can only be called on a block at the tip. We do this to keep the API simple.
   911  	blockHash, err := desoBlock.Header.Hash()
   912  	if err != nil {
   913  		return fmt.Errorf("DisconnectBlock: Problem computing block hash")
   914  	}
   915  	if *bav.TipHash != *blockHash {
   916  		return fmt.Errorf("DisconnectBlock: Block being disconnected does not match tip")
   917  	}
   918  
   919  	// Verify the number of ADD and SPEND operations in the utxOps list is equal
   920  	// to the number of outputs and inputs in the block respectively.
   921  	//
   922  	// There is a special case, which is that BidderInputs count as inputs in a
   923  	// txn and they result in SPEND operations being created.
   924  	numInputs := 0
   925  	numOutputs := 0
   926  	for _, txn := range desoBlock.Txns {
   927  		numInputs += len(txn.TxInputs)
   928  		if txn.TxnMeta.GetTxnType() == TxnTypeAcceptNFTBid {
   929  			numInputs += len(txn.TxnMeta.(*AcceptNFTBidMetadata).BidderInputs)
   930  		}
   931  		numOutputs += len(txn.TxOutputs)
   932  	}
   933  	numSpendOps := 0
   934  	numAddOps := 0
   935  	for _, utxoOpsForTxn := range utxoOps {
   936  		for _, op := range utxoOpsForTxn {
   937  			if op.Type == OperationTypeSpendUtxo {
   938  				numSpendOps++
   939  			} else if op.Type == OperationTypeAddUtxo {
   940  				numAddOps++
   941  			}
   942  		}
   943  	}
   944  	if numInputs != numSpendOps {
   945  		return fmt.Errorf(
   946  			"DisconnectBlock: Number of inputs in passed block (%d) "+
   947  				"not equal to number of SPEND operations in passed "+
   948  				"utxoOps (%d)", numInputs, numSpendOps)
   949  	}
   950  	// Note that the number of add operations can be greater than the number of "explicit"
   951  	// outputs in the block because transactions like BitcoinExchange
   952  	// produce "implicit" outputs when the transaction is applied.
   953  	if numOutputs > numAddOps {
   954  		return fmt.Errorf(
   955  			"DisconnectBlock: Number of outputs in passed block (%d) "+
   956  				"not equal to number of ADD operations in passed "+
   957  				"utxoOps (%d)", numOutputs, numAddOps)
   958  	}
   959  
   960  	// Loop through the txns backwards to process them.
   961  	// Track the operation we're performing as we go.
   962  	for txnIndex := len(desoBlock.Txns) - 1; txnIndex >= 0; txnIndex-- {
   963  		currentTxn := desoBlock.Txns[txnIndex]
   964  		txnHash := txHashes[txnIndex]
   965  		utxoOpsForTxn := utxoOps[txnIndex]
   966  		blockHeight := desoBlock.Header.Height
   967  
   968  		err := bav.DisconnectTransaction(currentTxn, txnHash, utxoOpsForTxn, uint32(blockHeight))
   969  		if err != nil {
   970  			return errors.Wrapf(err, "DisconnectBlock: Problem disconnecting transaction: %v", currentTxn)
   971  		}
   972  	}
   973  
   974  	// At this point, all of the transactions in the block should be fully
   975  	// reversed and the view should therefore be in the state it was in before
   976  	// this block was applied.
   977  
   978  	// Update the tip to point to the parent of this block since we've managed
   979  	// to successfully disconnect it.
   980  	bav.TipHash = desoBlock.Header.PrevBlockHash
   981  
   982  	return nil
   983  }
   984  
   985  func _isEntryImmatureBlockReward(utxoEntry *UtxoEntry, blockHeight uint32, params *DeSoParams) bool {
   986  	if utxoEntry.UtxoType == UtxoTypeBlockReward {
   987  		blocksPassed := blockHeight - utxoEntry.BlockHeight
   988  		// Note multiplication is OK here and has no chance of overflowing because
   989  		// block heights are computed by our code and are guaranteed to be sane values.
   990  		timePassed := time.Duration(int64(params.TimeBetweenBlocks) * int64(blocksPassed))
   991  		if timePassed < params.BlockRewardMaturity {
   992  			// Mark the block as invalid and return error if an immature block reward
   993  			// is being spent.
   994  			return true
   995  		}
   996  	}
   997  	return false
   998  }
   999  
  1000  func (bav *UtxoView) _verifySignature(txn *MsgDeSoTxn, blockHeight uint32) error {
  1001  	// Compute a hash of the transaction.
  1002  	txBytes, err := txn.ToBytes(true /*preSignature*/)
  1003  	if err != nil {
  1004  		return errors.Wrapf(err, "_verifySignature: Problem serializing txn without signature: ")
  1005  	}
  1006  	txHash := Sha256DoubleHash(txBytes)
  1007  
  1008  	// Look for the derived key in transaction ExtraData and validate it. For transactions
  1009  	// signed using a derived key, the derived public key is passed to ExtraData.
  1010  	var derivedPk *btcec.PublicKey
  1011  	var derivedPkBytes []byte
  1012  	if txn.ExtraData != nil {
  1013  		var isDerived bool
  1014  		derivedPkBytes, isDerived = txn.ExtraData[DerivedPublicKey]
  1015  		if isDerived {
  1016  			derivedPk, err = btcec.ParsePubKey(derivedPkBytes, btcec.S256())
  1017  			if err != nil {
  1018  				return RuleErrorDerivedKeyInvalidExtraData
  1019  			}
  1020  		}
  1021  	}
  1022  
  1023  	// Get the owner public key and attempt turning it into *btcec.PublicKey.
  1024  	ownerPkBytes := txn.PublicKey
  1025  	ownerPk, err := btcec.ParsePubKey(ownerPkBytes, btcec.S256())
  1026  	if err != nil {
  1027  		return errors.Wrapf(err, "_verifySignature: Problem parsing owner public key: ")
  1028  	}
  1029  
  1030  	// If no derived key is present in ExtraData, we check if transaction was signed by the owner.
  1031  	// If derived key is present in ExtraData, we check if transaction was signed by the derived key.
  1032  	if derivedPk == nil {
  1033  		// Verify that the transaction is signed by the specified key.
  1034  		if txn.Signature.Verify(txHash[:], ownerPk) {
  1035  			return nil
  1036  		}
  1037  	} else {
  1038  		// Look for a derived key entry in UtxoView and DB, check if it exists nor is deleted.
  1039  		derivedKeyEntry := bav._getDerivedKeyMappingForOwner(ownerPkBytes, derivedPkBytes)
  1040  		if derivedKeyEntry == nil || derivedKeyEntry.isDeleted {
  1041  			return RuleErrorDerivedKeyNotAuthorized
  1042  		}
  1043  
  1044  		// Sanity-check that transaction public keys line up with looked-up derivedKeyEntry public keys.
  1045  		if !reflect.DeepEqual(ownerPkBytes, derivedKeyEntry.OwnerPublicKey[:]) ||
  1046  			!reflect.DeepEqual(derivedPkBytes, derivedKeyEntry.DerivedPublicKey[:]) {
  1047  			return RuleErrorDerivedKeyNotAuthorized
  1048  		}
  1049  
  1050  		// At this point, we know the derivedKeyEntry that we have is matching.
  1051  		// We check if the derived key hasn't been de-authorized or hasn't expired.
  1052  		if derivedKeyEntry.OperationType != AuthorizeDerivedKeyOperationValid ||
  1053  			derivedKeyEntry.ExpirationBlock <= uint64(blockHeight) {
  1054  			return RuleErrorDerivedKeyNotAuthorized
  1055  		}
  1056  
  1057  		// All checks passed so we try to verify the signature.
  1058  		if txn.Signature.Verify(txHash[:], derivedPk) {
  1059  			return nil
  1060  		}
  1061  
  1062  		return RuleErrorDerivedKeyNotAuthorized
  1063  	}
  1064  
  1065  	return RuleErrorInvalidTransactionSignature
  1066  }
  1067  
  1068  func (bav *UtxoView) _connectBasicTransfer(
  1069  	txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) (
  1070  	_totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) {
  1071  
  1072  	var utxoOpsForTxn []*UtxoOperation
  1073  
  1074  	// Loop through all the inputs and validate them.
  1075  	var totalInput uint64
  1076  	// Each input should have a UtxoEntry corresponding to it if the transaction
  1077  	// is legitimate. These should all have back-pointers to their UtxoKeys as well.
  1078  	utxoEntriesForInputs := []*UtxoEntry{}
  1079  	for _, desoInput := range txn.TxInputs {
  1080  		// Fetch the utxoEntry for this input from the view. Make a copy to
  1081  		// avoid having the iterator change under our feet.
  1082  		utxoKey := UtxoKey(*desoInput)
  1083  		utxoEntry := bav.GetUtxoEntryForUtxoKey(&utxoKey)
  1084  		// If the utxo doesn't exist mark the block as invalid and return an error.
  1085  		if utxoEntry == nil {
  1086  			return 0, 0, nil, RuleErrorInputSpendsNonexistentUtxo
  1087  		}
  1088  		// If the utxo exists but is already spent mark the block as invalid and
  1089  		// return an error.
  1090  		if utxoEntry.isSpent {
  1091  			return 0, 0, nil, RuleErrorInputSpendsPreviouslySpentOutput
  1092  		}
  1093  		// If the utxo is from a block reward txn, make sure enough time has passed to
  1094  		// make it spendable.
  1095  		if _isEntryImmatureBlockReward(utxoEntry, blockHeight, bav.Params) {
  1096  			glog.V(1).Infof("utxoKey: %v, utxoEntry: %v, height: %d", &utxoKey, utxoEntry, blockHeight)
  1097  			return 0, 0, nil, RuleErrorInputSpendsImmatureBlockReward
  1098  		}
  1099  
  1100  		// Verify that the input's public key is the same as the public key specified
  1101  		// in the transaction.
  1102  		//
  1103  		// TODO: Enforcing this rule isn't a clear-cut decision. On the one hand,
  1104  		// we save space and minimize complexity by enforcing this constraint. On
  1105  		// the other hand, we make certain things harder to implement in the
  1106  		// future. For example, implementing constant key rotation like Bitcoin
  1107  		// has is difficult to do with a scheme like this. As are things like
  1108  		// multi-sig (although that could probably be handled using transaction
  1109  		// metadata). Key rotation combined with the use of addresses also helps
  1110  		// a lot with quantum resistance. Nevertheless, if we assume the platform
  1111  		// is committed to "one identity = roughly one public key" for usability
  1112  		// reasons (e.g. reputation is way easier to manage without key rotation),
  1113  		// then I don't think this constraint should pose much of an issue.
  1114  		if !reflect.DeepEqual(utxoEntry.PublicKey, txn.PublicKey) {
  1115  			return 0, 0, nil, RuleErrorInputWithPublicKeyDifferentFromTxnPublicKey
  1116  		}
  1117  
  1118  		// Sanity check the amount of the input.
  1119  		if utxoEntry.AmountNanos > MaxNanos ||
  1120  			totalInput >= (math.MaxUint64-utxoEntry.AmountNanos) ||
  1121  			totalInput+utxoEntry.AmountNanos > MaxNanos {
  1122  
  1123  			return 0, 0, nil, RuleErrorInputSpendsOutputWithInvalidAmount
  1124  		}
  1125  		// Add the amount of the utxo to the total input and add the UtxoEntry to
  1126  		// our list.
  1127  		totalInput += utxoEntry.AmountNanos
  1128  		utxoEntriesForInputs = append(utxoEntriesForInputs, utxoEntry)
  1129  
  1130  		// At this point we know the utxo exists in the view and is unspent so actually
  1131  		// tell the view to spend the input. If the spend fails for any reason we return
  1132  		// an error. Don't mark the block as invalid though since this is not necessarily
  1133  		// a rule error and the block could benefit from reprocessing.
  1134  		newUtxoOp, err := bav._spendUtxo(&utxoKey)
  1135  
  1136  		if err != nil {
  1137  			return 0, 0, nil, errors.Wrapf(err, "_connectBasicTransfer: Problem spending input utxo")
  1138  		}
  1139  
  1140  		utxoOpsForTxn = append(utxoOpsForTxn, newUtxoOp)
  1141  	}
  1142  
  1143  	if len(txn.TxInputs) != len(utxoEntriesForInputs) {
  1144  		// Something went wrong if these lists differ in length.
  1145  		return 0, 0, nil, fmt.Errorf("_connectBasicTransfer: Length of list of " +
  1146  			"UtxoEntries does not match length of input list; this should never happen")
  1147  	}
  1148  
  1149  	// Block rewards are a bit special in that we don't allow them to have any
  1150  	// inputs. Part of the reason for this stems from the fact that we explicitly
  1151  	// require that block reward transactions not be signed. If a block reward is
  1152  	// not allowed to have a signature then it should not be trying to spend any
  1153  	// inputs.
  1154  	if txn.TxnMeta.GetTxnType() == TxnTypeBlockReward && len(txn.TxInputs) != 0 {
  1155  		return 0, 0, nil, RuleErrorBlockRewardTxnNotAllowedToHaveInputs
  1156  	}
  1157  
  1158  	// At this point, all of the utxos corresponding to inputs of this txn
  1159  	// should be marked as spent in the view. Now we go through and process
  1160  	// the outputs.
  1161  	var totalOutput uint64
  1162  	amountsByPublicKey := make(map[PkMapKey]uint64)
  1163  	for outputIndex, desoOutput := range txn.TxOutputs {
  1164  		// Sanity check the amount of the output. Mark the block as invalid and
  1165  		// return an error if it isn't sane.
  1166  		if desoOutput.AmountNanos > MaxNanos ||
  1167  			totalOutput >= (math.MaxUint64-desoOutput.AmountNanos) ||
  1168  			totalOutput+desoOutput.AmountNanos > MaxNanos {
  1169  
  1170  			return 0, 0, nil, RuleErrorTxnOutputWithInvalidAmount
  1171  		}
  1172  
  1173  		// Since the amount is sane, add it to the total.
  1174  		totalOutput += desoOutput.AmountNanos
  1175  
  1176  		// Create a map of total output by public key. This is used to check diamond
  1177  		// amounts below.
  1178  		//
  1179  		// Note that we don't need to check overflow here because overflow is checked
  1180  		// directly above when adding to totalOutput.
  1181  		currentAmount, _ := amountsByPublicKey[MakePkMapKey(desoOutput.PublicKey)]
  1182  		amountsByPublicKey[MakePkMapKey(desoOutput.PublicKey)] = currentAmount + desoOutput.AmountNanos
  1183  
  1184  		// Create a new entry for this output and add it to the view. It should be
  1185  		// added at the end of the utxo list.
  1186  		outputKey := UtxoKey{
  1187  			TxID:  *txHash,
  1188  			Index: uint32(outputIndex),
  1189  		}
  1190  		utxoType := UtxoTypeOutput
  1191  		if txn.TxnMeta.GetTxnType() == TxnTypeBlockReward {
  1192  			utxoType = UtxoTypeBlockReward
  1193  		}
  1194  		// A basic transfer cannot create any output other than a "normal" output
  1195  		// or a BlockReward. Outputs of other types must be created after processing
  1196  		// the "basic" outputs.
  1197  
  1198  		utxoEntry := UtxoEntry{
  1199  			AmountNanos: desoOutput.AmountNanos,
  1200  			PublicKey:   desoOutput.PublicKey,
  1201  			BlockHeight: blockHeight,
  1202  			UtxoType:    utxoType,
  1203  			UtxoKey:     &outputKey,
  1204  			// We leave the position unset and isSpent to false by default.
  1205  			// The position will be set in the call to _addUtxo.
  1206  		}
  1207  		// If we have a problem adding this utxo return an error but don't
  1208  		// mark this block as invalid since it's not a rule error and the block
  1209  		// could therefore benefit from being processed in the future.
  1210  		newUtxoOp, err := bav._addUtxo(&utxoEntry)
  1211  		if err != nil {
  1212  			return 0, 0, nil, errors.Wrapf(err, "_connectBasicTransfer: Problem adding output utxo")
  1213  		}
  1214  
  1215  		// Rosetta uses this UtxoOperation to provide INPUT amounts
  1216  		utxoOpsForTxn = append(utxoOpsForTxn, newUtxoOp)
  1217  	}
  1218  
  1219  	// Now that we have computed the outputs, we can finish processing diamonds if need be.
  1220  	diamondPostHashBytes, hasDiamondPostHash := txn.ExtraData[DiamondPostHashKey]
  1221  	diamondPostHash := &BlockHash{}
  1222  	diamondLevelBytes, hasDiamondLevel := txn.ExtraData[DiamondLevelKey]
  1223  	var previousDiamondPostEntry *PostEntry
  1224  	var previousDiamondEntry *DiamondEntry
  1225  	if hasDiamondPostHash && blockHeight > DeSoDiamondsBlockHeight &&
  1226  		txn.TxnMeta.GetTxnType() == TxnTypeBasicTransfer {
  1227  		if !hasDiamondLevel {
  1228  			return 0, 0, nil, RuleErrorBasicTransferHasDiamondPostHashWithoutDiamondLevel
  1229  		}
  1230  		diamondLevel, bytesRead := Varint(diamondLevelBytes)
  1231  		// NOTE: Despite being an int, diamondLevel is required to be non-negative. This
  1232  		// is useful for sorting our dbkeys by diamondLevel.
  1233  		if bytesRead < 0 || diamondLevel < 0 {
  1234  			return 0, 0, nil, RuleErrorBasicTransferHasInvalidDiamondLevel
  1235  		}
  1236  
  1237  		// Get the post that is being diamonded.
  1238  		if len(diamondPostHashBytes) != HashSizeBytes {
  1239  			return 0, 0, nil, errors.Wrapf(
  1240  				RuleErrorBasicTransferDiamondInvalidLengthForPostHashBytes,
  1241  				"_connectBasicTransfer: DiamondPostHashBytes length: %d", len(diamondPostHashBytes))
  1242  		}
  1243  		copy(diamondPostHash[:], diamondPostHashBytes[:])
  1244  
  1245  		previousDiamondPostEntry = bav.GetPostEntryForPostHash(diamondPostHash)
  1246  		if previousDiamondPostEntry == nil || previousDiamondPostEntry.isDeleted {
  1247  			return 0, 0, nil, RuleErrorBasicTransferDiamondPostEntryDoesNotExist
  1248  		}
  1249  
  1250  		// Store the diamond recipient pub key so we can figure out how much they are paid.
  1251  		diamondRecipientPubKey := previousDiamondPostEntry.PosterPublicKey
  1252  
  1253  		// Check that the diamond sender and receiver public keys are different.
  1254  		if reflect.DeepEqual(txn.PublicKey, diamondRecipientPubKey) {
  1255  			return 0, 0, nil, RuleErrorBasicTransferDiamondCannotTransferToSelf
  1256  		}
  1257  
  1258  		expectedDeSoNanosToTransfer, netNewDiamonds, err := bav.ValidateDiamondsAndGetNumDeSoNanos(
  1259  			txn.PublicKey, diamondRecipientPubKey, diamondPostHash, diamondLevel, blockHeight)
  1260  		if err != nil {
  1261  			return 0, 0, nil, errors.Wrapf(err, "_connectBasicTransfer: ")
  1262  		}
  1263  		diamondRecipientTotal, _ := amountsByPublicKey[MakePkMapKey(diamondRecipientPubKey)]
  1264  
  1265  		if diamondRecipientTotal < expectedDeSoNanosToTransfer {
  1266  			return 0, 0, nil, RuleErrorBasicTransferInsufficientDeSoForDiamondLevel
  1267  		}
  1268  
  1269  		// The diamondPostEntry needs to be updated with the number of new diamonds.
  1270  		// We make a copy to avoid issues with disconnecting.
  1271  		newDiamondPostEntry := &PostEntry{}
  1272  		*newDiamondPostEntry = *previousDiamondPostEntry
  1273  		newDiamondPostEntry.DiamondCount += uint64(netNewDiamonds)
  1274  		bav._setPostEntryMappings(newDiamondPostEntry)
  1275  
  1276  		// Convert pub keys into PKIDs so we can make the DiamondEntry.
  1277  		senderPKID := bav.GetPKIDForPublicKey(txn.PublicKey)
  1278  		receiverPKID := bav.GetPKIDForPublicKey(diamondRecipientPubKey)
  1279  
  1280  		// Create a new DiamondEntry
  1281  		newDiamondEntry := &DiamondEntry{
  1282  			SenderPKID:      senderPKID.PKID,
  1283  			ReceiverPKID:    receiverPKID.PKID,
  1284  			DiamondPostHash: diamondPostHash,
  1285  			DiamondLevel:    diamondLevel,
  1286  		}
  1287  
  1288  		// Save the old DiamondEntry
  1289  		diamondKey := MakeDiamondKey(senderPKID.PKID, receiverPKID.PKID, diamondPostHash)
  1290  		existingDiamondEntry := bav.GetDiamondEntryForDiamondKey(&diamondKey)
  1291  		// Save the existing DiamondEntry, if it exists, so we can disconnect
  1292  		if existingDiamondEntry != nil {
  1293  			dd := &DiamondEntry{}
  1294  			*dd = *existingDiamondEntry
  1295  			previousDiamondEntry = dd
  1296  		}
  1297  
  1298  		// Now set the diamond entry mappings on the view so they are flushed to the DB.
  1299  		bav._setDiamondEntryMappings(newDiamondEntry)
  1300  
  1301  		// Add an op to help us with the disconnect.
  1302  		utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{
  1303  			Type:             OperationTypeDeSoDiamond,
  1304  			PrevPostEntry:    previousDiamondPostEntry,
  1305  			PrevDiamondEntry: previousDiamondEntry,
  1306  		})
  1307  	}
  1308  
  1309  	// If signature verification is requested then do that as well.
  1310  	if verifySignatures {
  1311  		// When we looped through the inputs we verified that all of them belong
  1312  		// to the public key specified in the transaction. So, as long as the transaction
  1313  		// public key has signed the transaction as a whole, we can assume that
  1314  		// all of the inputs are authorized to be spent. One signature to rule them
  1315  		// all.
  1316  		//
  1317  		// UPDATE: Transaction can be signed by a different key, called a derived key.
  1318  		// The derived key must be authorized through an AuthorizeDerivedKey transaction,
  1319  		// and then passed along in ExtraData for evey transaction signed with it.
  1320  		//
  1321  		// We treat block rewards as a special case in that we actually require that they
  1322  		// not have a transaction-level public key and that they not be signed. Doing this
  1323  		// simplifies things operationally for miners because it means they can run their
  1324  		// mining operation without having any private key material on any of the mining
  1325  		// nodes. Block rewards are the only transactions that get a pass on this. They are
  1326  		// also not allowed to have any inputs because they by construction cannot authorize
  1327  		// the spending of any inputs.
  1328  		if txn.TxnMeta.GetTxnType() == TxnTypeBlockReward {
  1329  			if len(txn.PublicKey) != 0 || txn.Signature != nil {
  1330  				return 0, 0, nil, RuleErrorBlockRewardTxnNotAllowedToHaveSignature
  1331  			}
  1332  		} else {
  1333  			if err := bav._verifySignature(txn, blockHeight); err != nil {
  1334  				return 0, 0, nil, errors.Wrapf(err, "_connectBasicTransfer: Problem verifying txn signature: ")
  1335  			}
  1336  		}
  1337  	}
  1338  
  1339  	// Now that we've processed the transaction, return all of the computed
  1340  	// data.
  1341  	return totalInput, totalOutput, utxoOpsForTxn, nil
  1342  }
  1343  
  1344  func (bav *UtxoView) _connectUpdateGlobalParams(
  1345  	txn *MsgDeSoTxn, txHash *BlockHash, blockHeight uint32, verifySignatures bool) (
  1346  	_totalInput uint64, _totalOutput uint64, _utxoOps []*UtxoOperation, _err error) {
  1347  
  1348  	// Check that the transaction has the right TxnType.
  1349  	if txn.TxnMeta.GetTxnType() != TxnTypeUpdateGlobalParams {
  1350  		return 0, 0, nil, fmt.Errorf("_connectUpdateGlobalParams: called with bad TxnType %s",
  1351  			txn.TxnMeta.GetTxnType().String())
  1352  	}
  1353  
  1354  	// Initialize the new global params entry as a copy of the old global params entry and
  1355  	// only overwrite values provided in extra data.
  1356  	prevGlobalParamsEntry := bav.GlobalParamsEntry
  1357  	newGlobalParamsEntry := *prevGlobalParamsEntry
  1358  	extraData := txn.ExtraData
  1359  	// Validate the public key. Only a paramUpdater is allowed to trigger this.
  1360  	_, updaterIsParamUpdater := bav.Params.ParamUpdaterPublicKeys[MakePkMapKey(txn.PublicKey)]
  1361  	if !updaterIsParamUpdater {
  1362  		return 0, 0, nil, RuleErrorUserNotAuthorizedToUpdateGlobalParams
  1363  	}
  1364  	if len(extraData[USDCentsPerBitcoinKey]) > 0 {
  1365  		// Validate that the exchange rate is not less than the floor as a sanity-check.
  1366  		newUSDCentsPerBitcoin, usdCentsPerBitcoinBytesRead := Uvarint(extraData[USDCentsPerBitcoinKey])
  1367  		if usdCentsPerBitcoinBytesRead <= 0 {
  1368  			return 0, 0, nil, fmt.Errorf("_connectUpdateGlobalParams: unable to decode USDCentsPerBitcoin as uint64")
  1369  		}
  1370  		if newUSDCentsPerBitcoin < MinUSDCentsPerBitcoin {
  1371  			return 0, 0, nil, RuleErrorExchangeRateTooLow
  1372  		}
  1373  		if newUSDCentsPerBitcoin > MaxUSDCentsPerBitcoin {
  1374  			return 0, 0, nil, RuleErrorExchangeRateTooHigh
  1375  		}
  1376  		newGlobalParamsEntry.USDCentsPerBitcoin = newUSDCentsPerBitcoin
  1377  	}
  1378  
  1379  	if len(extraData[MinNetworkFeeNanosPerKBKey]) > 0 {
  1380  		newMinNetworkFeeNanosPerKB, minNetworkFeeNanosPerKBBytesRead := Uvarint(extraData[MinNetworkFeeNanosPerKBKey])
  1381  		if minNetworkFeeNanosPerKBBytesRead <= 0 {
  1382  			return 0, 0, nil, fmt.Errorf("_connectUpdateGlobalParams: unable to decode MinNetworkFeeNanosPerKB as uint64")
  1383  		}
  1384  		if newMinNetworkFeeNanosPerKB < MinNetworkFeeNanosPerKBValue {
  1385  			return 0, 0, nil, RuleErrorMinNetworkFeeTooLow
  1386  		}
  1387  		if newMinNetworkFeeNanosPerKB > MaxNetworkFeeNanosPerKBValue {
  1388  			return 0, 0, nil, RuleErrorMinNetworkFeeTooHigh
  1389  		}
  1390  		newGlobalParamsEntry.MinimumNetworkFeeNanosPerKB = newMinNetworkFeeNanosPerKB
  1391  	}
  1392  
  1393  	if len(extraData[CreateProfileFeeNanosKey]) > 0 {
  1394  		newCreateProfileFeeNanos, createProfileFeeNanosBytesRead := Uvarint(extraData[CreateProfileFeeNanosKey])
  1395  		if createProfileFeeNanosBytesRead <= 0 {
  1396  			return 0, 0, nil, fmt.Errorf("_connectUpdateGlobalParams: unable to decode CreateProfileFeeNanos as uint64")
  1397  		}
  1398  		if newCreateProfileFeeNanos < MinCreateProfileFeeNanos {
  1399  			return 0, 0, nil, RuleErrorCreateProfileFeeTooLow
  1400  		}
  1401  		if newCreateProfileFeeNanos > MaxCreateProfileFeeNanos {
  1402  			return 0, 0, nil, RuleErrorCreateProfileTooHigh
  1403  		}
  1404  		newGlobalParamsEntry.CreateProfileFeeNanos = newCreateProfileFeeNanos
  1405  	}
  1406  
  1407  	if len(extraData[CreateNFTFeeNanosKey]) > 0 {
  1408  		newCreateNFTFeeNanos, createNFTFeeNanosBytesRead := Uvarint(extraData[CreateNFTFeeNanosKey])
  1409  		if createNFTFeeNanosBytesRead <= 0 {
  1410  			return 0, 0, nil, fmt.Errorf("_connectUpdateGlobalParams: unable to decode CreateNFTFeeNanos as uint64")
  1411  		}
  1412  		if newCreateNFTFeeNanos < MinCreateNFTFeeNanos {
  1413  			return 0, 0, nil, RuleErrorCreateNFTFeeTooLow
  1414  		}
  1415  		if newCreateNFTFeeNanos > MaxCreateNFTFeeNanos {
  1416  			return 0, 0, nil, RuleErrorCreateNFTFeeTooHigh
  1417  		}
  1418  		newGlobalParamsEntry.CreateNFTFeeNanos = newCreateNFTFeeNanos
  1419  	}
  1420  
  1421  	if len(extraData[MaxCopiesPerNFTKey]) > 0 {
  1422  		newMaxCopiesPerNFT, maxCopiesPerNFTBytesRead := Uvarint(extraData[MaxCopiesPerNFTKey])
  1423  		if maxCopiesPerNFTBytesRead <= 0 {
  1424  			return 0, 0, nil, fmt.Errorf("_connectUpdateGlobalParams: unable to decode MaxCopiesPerNFT as uint64")
  1425  		}
  1426  		if newMaxCopiesPerNFT < MinMaxCopiesPerNFT {
  1427  			return 0, 0, nil, RuleErrorMaxCopiesPerNFTTooLow
  1428  		}
  1429  		if newMaxCopiesPerNFT > MaxMaxCopiesPerNFT {
  1430  			return 0, 0, nil, RuleErrorMaxCopiesPerNFTTooHigh
  1431  		}
  1432  		newGlobalParamsEntry.MaxCopiesPerNFT = newMaxCopiesPerNFT
  1433  	}
  1434  
  1435  	var newForbiddenPubKeyEntry *ForbiddenPubKeyEntry
  1436  	var prevForbiddenPubKeyEntry *ForbiddenPubKeyEntry
  1437  	var forbiddenPubKey []byte
  1438  	if _, exists := extraData[ForbiddenBlockSignaturePubKeyKey]; exists {
  1439  		forbiddenPubKey = extraData[ForbiddenBlockSignaturePubKeyKey]
  1440  
  1441  		if len(forbiddenPubKey) != btcec.PubKeyBytesLenCompressed {
  1442  			return 0, 0, nil, RuleErrorForbiddenPubKeyLength
  1443  		}
  1444  
  1445  		// If there is already an entry on the view for this pub key, save it.
  1446  		if val, ok := bav.ForbiddenPubKeyToForbiddenPubKeyEntry[MakePkMapKey(forbiddenPubKey)]; ok {
  1447  			prevForbiddenPubKeyEntry = val
  1448  		}
  1449  
  1450  		newForbiddenPubKeyEntry = &ForbiddenPubKeyEntry{
  1451  			PubKey: forbiddenPubKey,
  1452  		}
  1453  	}
  1454  
  1455  	// Connect basic txn to get the total input and the total output without
  1456  	// considering the transaction metadata.
  1457  	totalInput, totalOutput, utxoOpsForTxn, err := bav._connectBasicTransfer(
  1458  		txn, txHash, blockHeight, verifySignatures)
  1459  	if err != nil {
  1460  		return 0, 0, nil, errors.Wrapf(err, "_connectUpdateGlobalParams: ")
  1461  	}
  1462  
  1463  	// Output must be non-zero
  1464  	if totalOutput == 0 {
  1465  		return 0, 0, nil, RuleErrorUserOutputMustBeNonzero
  1466  	}
  1467  
  1468  	if verifySignatures {
  1469  		// _connectBasicTransfer has already checked that the transaction is
  1470  		// signed by the top-level public key, which is all we need.
  1471  	}
  1472  
  1473  	// Update the GlobalParamsEntry using the txn's ExtraData. Save the previous value
  1474  	// so it can be easily reverted.
  1475  	bav.GlobalParamsEntry = &newGlobalParamsEntry
  1476  
  1477  	// Update the forbidden pub key entry on the view, if we have one to update.
  1478  	if newForbiddenPubKeyEntry != nil {
  1479  		bav.ForbiddenPubKeyToForbiddenPubKeyEntry[MakePkMapKey(forbiddenPubKey)] = newForbiddenPubKeyEntry
  1480  	}
  1481  
  1482  	// Save a UtxoOperation of type OperationTypeUpdateGlobalParams that will allow
  1483  	// us to easily revert when we disconnect the transaction.
  1484  	utxoOpsForTxn = append(utxoOpsForTxn, &UtxoOperation{
  1485  		Type:                     OperationTypeUpdateGlobalParams,
  1486  		PrevGlobalParamsEntry:    prevGlobalParamsEntry,
  1487  		PrevForbiddenPubKeyEntry: prevForbiddenPubKeyEntry,
  1488  	})
  1489  
  1490  	return totalInput, totalOutput, utxoOpsForTxn, nil
  1491  }
  1492  
  1493  func (bav *UtxoView) ValidateDiamondsAndGetNumDeSoNanos(
  1494  	senderPublicKey []byte,
  1495  	receiverPublicKey []byte,
  1496  	diamondPostHash *BlockHash,
  1497  	diamondLevel int64,
  1498  	blockHeight uint32,
  1499  ) (_numDeSoNanos uint64, _netNewDiamonds int64, _err error) {
  1500  
  1501  	// Check that the diamond level is reasonable
  1502  	diamondLevelMap := GetDeSoNanosDiamondLevelMapAtBlockHeight(int64(blockHeight))
  1503  	if _, isAllowedLevel := diamondLevelMap[diamondLevel]; !isAllowedLevel {
  1504  		return 0, 0, fmt.Errorf(
  1505  			"ValidateDiamondsAndGetNumCreatorCoinNanos: Diamond level %v not allowed",
  1506  			diamondLevel)
  1507  	}
  1508  
  1509  	// Convert pub keys into PKIDs.
  1510  	senderPKID := bav.GetPKIDForPublicKey(senderPublicKey)
  1511  	receiverPKID := bav.GetPKIDForPublicKey(receiverPublicKey)
  1512  
  1513  	// Look up if there is an existing diamond entry.
  1514  	diamondKey := MakeDiamondKey(senderPKID.PKID, receiverPKID.PKID, diamondPostHash)
  1515  	diamondEntry := bav.GetDiamondEntryForDiamondKey(&diamondKey)
  1516  
  1517  	currDiamondLevel := int64(0)
  1518  	if diamondEntry != nil {
  1519  		currDiamondLevel = diamondEntry.DiamondLevel
  1520  	}
  1521  
  1522  	if currDiamondLevel >= diamondLevel {
  1523  		return 0, 0, RuleErrorCreatorCoinTransferPostAlreadyHasSufficientDiamonds
  1524  	}
  1525  
  1526  	// Calculate the number of creator coin nanos needed vs. already added for previous diamonds.
  1527  	currDeSoNanos := GetDeSoNanosForDiamondLevelAtBlockHeight(currDiamondLevel, int64(blockHeight))
  1528  	neededDeSoNanos := GetDeSoNanosForDiamondLevelAtBlockHeight(diamondLevel, int64(blockHeight))
  1529  
  1530  	// There is an edge case where, if the person's creator coin value goes down
  1531  	// by a large enough amount, then they can get a "free" diamond upgrade. This
  1532  	// seems fine for now.
  1533  	desoToTransferNanos := uint64(0)
  1534  	if neededDeSoNanos > currDeSoNanos {
  1535  		desoToTransferNanos = neededDeSoNanos - currDeSoNanos
  1536  	}
  1537  
  1538  	netNewDiamonds := diamondLevel - currDiamondLevel
  1539  
  1540  	return desoToTransferNanos, netNewDiamonds, nil
  1541  }
  1542  
  1543  func (bav *UtxoView) ConnectTransaction(txn *MsgDeSoTxn, txHash *BlockHash,
  1544  	txnSizeBytes int64,
  1545  	blockHeight uint32, verifySignatures bool, ignoreUtxos bool) (
  1546  	_utxoOps []*UtxoOperation, _totalInput uint64, _totalOutput uint64,
  1547  	_fees uint64, _err error) {
  1548  
  1549  	return bav._connectTransaction(txn, txHash,
  1550  		txnSizeBytes,
  1551  		blockHeight, verifySignatures,
  1552  		ignoreUtxos)
  1553  
  1554  }
  1555  
  1556  func (bav *UtxoView) _connectTransaction(txn *MsgDeSoTxn, txHash *BlockHash,
  1557  	txnSizeBytes int64, blockHeight uint32, verifySignatures bool, ignoreUtxos bool) (
  1558  	_utxoOps []*UtxoOperation, _totalInput uint64, _totalOutput uint64,
  1559  	_fees uint64, _err error) {
  1560  
  1561  	// Do a quick sanity check before trying to connect.
  1562  	if err := CheckTransactionSanity(txn); err != nil {
  1563  		return nil, 0, 0, 0, errors.Wrapf(err, "_connectTransaction: ")
  1564  	}
  1565  
  1566  	// Don't allow transactions that take up more than half of the block.
  1567  	txnBytes, err := txn.ToBytes(false)
  1568  	if err != nil {
  1569  		return nil, 0, 0, 0, errors.Wrapf(
  1570  			err, "CheckTransactionSanity: Problem serializing transaction: ")
  1571  	}
  1572  	if len(txnBytes) > int(bav.Params.MaxBlockSizeBytes/2) {
  1573  		return nil, 0, 0, 0, RuleErrorTxnTooBig
  1574  	}
  1575  
  1576  	var totalInput, totalOutput uint64
  1577  	var utxoOpsForTxn []*UtxoOperation
  1578  	if txn.TxnMeta.GetTxnType() == TxnTypeBlockReward || txn.TxnMeta.GetTxnType() == TxnTypeBasicTransfer {
  1579  		totalInput, totalOutput, utxoOpsForTxn, err =
  1580  			bav._connectBasicTransfer(
  1581  				txn, txHash, blockHeight, verifySignatures)
  1582  
  1583  	} else if txn.TxnMeta.GetTxnType() == TxnTypeBitcoinExchange {
  1584  		totalInput, totalOutput, utxoOpsForTxn, err =
  1585  			bav._connectBitcoinExchange(
  1586  				txn, txHash, blockHeight, verifySignatures)
  1587  
  1588  	} else if txn.TxnMeta.GetTxnType() == TxnTypePrivateMessage {
  1589  		totalInput, totalOutput, utxoOpsForTxn, err =
  1590  			bav._connectPrivateMessage(
  1591  				txn, txHash, blockHeight, verifySignatures)
  1592  
  1593  	} else if txn.TxnMeta.GetTxnType() == TxnTypeSubmitPost {
  1594  		totalInput, totalOutput, utxoOpsForTxn, err =
  1595  			bav._connectSubmitPost(
  1596  				txn, txHash, blockHeight, verifySignatures, ignoreUtxos)
  1597  
  1598  	} else if txn.TxnMeta.GetTxnType() == TxnTypeUpdateProfile {
  1599  		totalInput, totalOutput, utxoOpsForTxn, err =
  1600  			bav._connectUpdateProfile(
  1601  				txn, txHash, blockHeight, verifySignatures, ignoreUtxos)
  1602  
  1603  	} else if txn.TxnMeta.GetTxnType() == TxnTypeUpdateBitcoinUSDExchangeRate {
  1604  		totalInput, totalOutput, utxoOpsForTxn, err =
  1605  			bav._connectUpdateBitcoinUSDExchangeRate(
  1606  				txn, txHash, blockHeight, verifySignatures)
  1607  
  1608  	} else if txn.TxnMeta.GetTxnType() == TxnTypeUpdateGlobalParams {
  1609  		totalInput, totalOutput, utxoOpsForTxn, err =
  1610  			bav._connectUpdateGlobalParams(
  1611  				txn, txHash, blockHeight, verifySignatures)
  1612  
  1613  	} else if txn.TxnMeta.GetTxnType() == TxnTypeFollow {
  1614  		totalInput, totalOutput, utxoOpsForTxn, err =
  1615  			bav._connectFollow(
  1616  				txn, txHash, blockHeight, verifySignatures)
  1617  
  1618  	} else if txn.TxnMeta.GetTxnType() == TxnTypeLike {
  1619  		totalInput, totalOutput, utxoOpsForTxn, err =
  1620  			bav._connectLike(txn, txHash, blockHeight, verifySignatures)
  1621  
  1622  	} else if txn.TxnMeta.GetTxnType() == TxnTypeCreatorCoin {
  1623  		totalInput, totalOutput, utxoOpsForTxn, err =
  1624  			bav._connectCreatorCoin(
  1625  				txn, txHash, blockHeight, verifySignatures)
  1626  
  1627  	} else if txn.TxnMeta.GetTxnType() == TxnTypeCreatorCoinTransfer {
  1628  		totalInput, totalOutput, utxoOpsForTxn, err =
  1629  			bav._connectCreatorCoinTransfer(
  1630  				txn, txHash, blockHeight, verifySignatures)
  1631  
  1632  	} else if txn.TxnMeta.GetTxnType() == TxnTypeSwapIdentity {
  1633  		totalInput, totalOutput, utxoOpsForTxn, err =
  1634  			bav._connectSwapIdentity(
  1635  				txn, txHash, blockHeight, verifySignatures)
  1636  
  1637  	} else if txn.TxnMeta.GetTxnType() == TxnTypeCreateNFT {
  1638  		totalInput, totalOutput, utxoOpsForTxn, err =
  1639  			bav._connectCreateNFT(
  1640  				txn, txHash, blockHeight, verifySignatures)
  1641  
  1642  	} else if txn.TxnMeta.GetTxnType() == TxnTypeUpdateNFT {
  1643  		totalInput, totalOutput, utxoOpsForTxn, err =
  1644  			bav._connectUpdateNFT(
  1645  				txn, txHash, blockHeight, verifySignatures)
  1646  
  1647  	} else if txn.TxnMeta.GetTxnType() == TxnTypeAcceptNFTBid {
  1648  		totalInput, totalOutput, utxoOpsForTxn, err =
  1649  			bav._connectAcceptNFTBid(
  1650  				txn, txHash, blockHeight, verifySignatures)
  1651  
  1652  	} else if txn.TxnMeta.GetTxnType() == TxnTypeNFTBid {
  1653  		totalInput, totalOutput, utxoOpsForTxn, err =
  1654  			bav._connectNFTBid(
  1655  				txn, txHash, blockHeight, verifySignatures)
  1656  
  1657  	} else if txn.TxnMeta.GetTxnType() == TxnTypeNFTTransfer {
  1658  		totalInput, totalOutput, utxoOpsForTxn, err =
  1659  			bav._connectNFTTransfer(
  1660  				txn, txHash, blockHeight, verifySignatures)
  1661  
  1662  	} else if txn.TxnMeta.GetTxnType() == TxnTypeAcceptNFTTransfer {
  1663  		totalInput, totalOutput, utxoOpsForTxn, err =
  1664  			bav._connectAcceptNFTTransfer(
  1665  				txn, txHash, blockHeight, verifySignatures)
  1666  
  1667  	} else if txn.TxnMeta.GetTxnType() == TxnTypeBurnNFT {
  1668  		totalInput, totalOutput, utxoOpsForTxn, err =
  1669  			bav._connectBurnNFT(
  1670  				txn, txHash, blockHeight, verifySignatures)
  1671  
  1672  	} else if txn.TxnMeta.GetTxnType() == TxnTypeAuthorizeDerivedKey {
  1673  		totalInput, totalOutput, utxoOpsForTxn, err =
  1674  			bav._connectAuthorizeDerivedKey(
  1675  				txn, txHash, blockHeight, verifySignatures)
  1676  
  1677  	} else {
  1678  		err = fmt.Errorf("ConnectTransaction: Unimplemented txn type %v", txn.TxnMeta.GetTxnType().String())
  1679  	}
  1680  	if err != nil {
  1681  		return nil, 0, 0, 0, errors.Wrapf(err, "ConnectTransaction: ")
  1682  	}
  1683  
  1684  	// Do some extra processing for non-block-reward transactions. Block reward transactions
  1685  	// will return zero for their fees.
  1686  	fees := uint64(0)
  1687  	if txn.TxnMeta.GetTxnType() != TxnTypeBlockReward {
  1688  		// If this isn't a block reward transaction, make sure the total input does
  1689  		// not exceed the total output. If it does, mark the block as invalid and
  1690  		// return an error.
  1691  		if totalInput < totalOutput {
  1692  			return nil, 0, 0, 0, RuleErrorTxnOutputExceedsInput
  1693  		}
  1694  		fees = totalInput - totalOutput
  1695  	}
  1696  
  1697  	// BitcoinExchange transactions have their own special fee that is computed as a function of how much
  1698  	// DeSo is being minted. They do not need to abide by the global minimum fee check, since if they had
  1699  	// enough fees to get mined into the Bitcoin blockchain itself then they're almost certainly not spam.
  1700  	// If the transaction size was set to 0, skip validating the fee is above the minimum.
  1701  	// If the current minimum network fee per kb is set to 0, that indicates we should not assess a minimum fee.
  1702  	if txn.TxnMeta.GetTxnType() != TxnTypeBitcoinExchange && txnSizeBytes != 0 && bav.GlobalParamsEntry.MinimumNetworkFeeNanosPerKB != 0 {
  1703  		// Make sure there isn't overflow in the fee.
  1704  		if fees != ((fees * 1000) / 1000) {
  1705  			return nil, 0, 0, 0, RuleErrorOverflowDetectedInFeeRateCalculation
  1706  		}
  1707  		// If the fee is less than the minimum network fee per KB, return an error.
  1708  		if (fees*1000)/uint64(txnSizeBytes) < bav.GlobalParamsEntry.MinimumNetworkFeeNanosPerKB {
  1709  			return nil, 0, 0, 0, RuleErrorTxnFeeBelowNetworkMinimum
  1710  		}
  1711  	}
  1712  
  1713  	return utxoOpsForTxn, totalInput, totalOutput, fees, nil
  1714  }
  1715  
  1716  func (bav *UtxoView) ConnectBlock(
  1717  	desoBlock *MsgDeSoBlock, txHashes []*BlockHash, verifySignatures bool, eventManager *EventManager) (
  1718  	[][]*UtxoOperation, error) {
  1719  
  1720  	glog.V(1).Infof("ConnectBlock: Connecting block %v", desoBlock)
  1721  
  1722  	// Check that the block being connected references the current tip. ConnectBlock
  1723  	// can only add a block to the current tip. We do this to keep the API simple.
  1724  	if *desoBlock.Header.PrevBlockHash != *bav.TipHash {
  1725  		return nil, fmt.Errorf("ConnectBlock: Parent hash of block being connected does not match tip")
  1726  	}
  1727  
  1728  	blockHeader := desoBlock.Header
  1729  	// Loop through all the transactions and validate them using the view. Also
  1730  	// keep track of the total fees throughout.
  1731  	var totalFees uint64
  1732  	utxoOps := [][]*UtxoOperation{}
  1733  	for txIndex, txn := range desoBlock.Txns {
  1734  		txHash := txHashes[txIndex]
  1735  
  1736  		// ConnectTransaction validates all of the transactions in the block and
  1737  		// is responsible for verifying signatures.
  1738  		//
  1739  		// TODO: We currently don't check that the min transaction fee is satisfied when
  1740  		// connecting blocks. We skip this check because computing the transaction's size
  1741  		// would slow down block processing significantly. We should figure out a way to
  1742  		// enforce this check in the future, but for now the only attack vector is one in
  1743  		// which a miner is trying to spam the network, which should generally never happen.
  1744  		utxoOpsForTxn, totalInput, totalOutput, currentFees, err := bav.ConnectTransaction(
  1745  			txn, txHash, 0, uint32(blockHeader.Height), verifySignatures, false /*ignoreUtxos*/)
  1746  		_, _ = totalInput, totalOutput // A bit surprising we don't use these
  1747  		if err != nil {
  1748  			return nil, errors.Wrapf(err, "ConnectBlock: ")
  1749  		}
  1750  
  1751  		// Add the fees from this txn to the total fees. If any overflow occurs
  1752  		// mark the block as invalid and return a rule error. Note that block reward
  1753  		// txns should count as having zero fees.
  1754  		if totalFees > (math.MaxUint64 - currentFees) {
  1755  			return nil, RuleErrorTxnOutputWithInvalidAmount
  1756  		}
  1757  		totalFees += currentFees
  1758  
  1759  		// Add the utxo operations to our list for all the txns.
  1760  		utxoOps = append(utxoOps, utxoOpsForTxn)
  1761  
  1762  		// TODO: This should really be called at the end of _connectTransaction but it's
  1763  		// really annoying to change all the call signatures right now and we don't really
  1764  		// need it just yet.
  1765  		//
  1766  		// Call the event manager
  1767  		if eventManager != nil {
  1768  			eventManager.transactionConnected(&TransactionEvent{
  1769  				Txn:      txn,
  1770  				TxnHash:  txHash,
  1771  				UtxoView: bav,
  1772  				UtxoOps:  utxoOpsForTxn,
  1773  			})
  1774  		}
  1775  	}
  1776  
  1777  	// We should now have computed totalFees. Use this to check that
  1778  	// the block reward's outputs are correct.
  1779  	//
  1780  	// Compute the sum of the outputs in the block reward. If an overflow
  1781  	// occurs mark the block as invalid and return a rule error.
  1782  	var blockRewardOutput uint64
  1783  	for _, bro := range desoBlock.Txns[0].TxOutputs {
  1784  		if bro.AmountNanos > MaxNanos ||
  1785  			blockRewardOutput > (math.MaxUint64-bro.AmountNanos) {
  1786  
  1787  			return nil, RuleErrorBlockRewardOutputWithInvalidAmount
  1788  		}
  1789  		blockRewardOutput += bro.AmountNanos
  1790  	}
  1791  	// Verify that the block reward does not overflow when added to
  1792  	// the block's fees.
  1793  	blockReward := CalcBlockRewardNanos(uint32(blockHeader.Height))
  1794  	if totalFees > MaxNanos ||
  1795  		blockReward > (math.MaxUint64-totalFees) {
  1796  
  1797  		return nil, RuleErrorBlockRewardOverflow
  1798  	}
  1799  	maxBlockReward := blockReward + totalFees
  1800  	// If the outputs of the block reward txn exceed the max block reward
  1801  	// allowed then mark the block as invalid and return an error.
  1802  	if blockRewardOutput > maxBlockReward {
  1803  		glog.Errorf("ConnectBlock(RuleErrorBlockRewardExceedsMaxAllowed): "+
  1804  			"blockRewardOutput %d exceeds maxBlockReward %d", blockRewardOutput, maxBlockReward)
  1805  		return nil, RuleErrorBlockRewardExceedsMaxAllowed
  1806  	}
  1807  
  1808  	// If we made it to the end and this block is valid, advance the tip
  1809  	// of the view to reflect that.
  1810  	blockHash, err := desoBlock.Header.Hash()
  1811  	if err != nil {
  1812  		return nil, fmt.Errorf("ConnectBlock: Problem computing block hash after validation")
  1813  	}
  1814  	bav.TipHash = blockHash
  1815  
  1816  	return utxoOps, nil
  1817  }
  1818  
  1819  // Preload tries to fetch all the relevant data needed to connect a block
  1820  // in batches from Postgres. It marks many objects as "nil" in the respective
  1821  // data structures and then fills in the objects it is able to retrieve from
  1822  // the database. It's much faster to fetch data in bulk and cache "nil" values
  1823  // then to query individual records when connecting every transaction. If something
  1824  // is not preloaded the view falls back to individual queries.
  1825  func (bav *UtxoView) Preload(desoBlock *MsgDeSoBlock) error {
  1826  	// We can only preload if we're using postgres
  1827  	if bav.Postgres == nil {
  1828  		return nil
  1829  	}
  1830  
  1831  	// One iteration for all the PKIDs
  1832  	// NOTE: Work in progress. Testing with follows for now.
  1833  	var publicKeys []*PublicKey
  1834  	for _, txn := range desoBlock.Txns {
  1835  		if txn.TxnMeta.GetTxnType() == TxnTypeFollow {
  1836  			txnMeta := txn.TxnMeta.(*FollowMetadata)
  1837  			publicKeys = append(publicKeys, NewPublicKey(txn.PublicKey))
  1838  			publicKeys = append(publicKeys, NewPublicKey(txnMeta.FollowedPublicKey))
  1839  		} else if txn.TxnMeta.GetTxnType() == TxnTypeCreatorCoin {
  1840  			txnMeta := txn.TxnMeta.(*CreatorCoinMetadataa)
  1841  			publicKeys = append(publicKeys, NewPublicKey(txn.PublicKey))
  1842  			publicKeys = append(publicKeys, NewPublicKey(txnMeta.ProfilePublicKey))
  1843  		} else if txn.TxnMeta.GetTxnType() == TxnTypeUpdateProfile {
  1844  			publicKeys = append(publicKeys, NewPublicKey(txn.PublicKey))
  1845  		}
  1846  	}
  1847  
  1848  	if len(publicKeys) > 0 {
  1849  		for _, publicKey := range publicKeys {
  1850  			publicKeyBytes := publicKey.ToBytes()
  1851  			pkidEntry := &PKIDEntry{
  1852  				PKID:      PublicKeyToPKID(publicKeyBytes),
  1853  				PublicKey: publicKeyBytes,
  1854  			}
  1855  
  1856  			// Set pkid entries for all the public keys
  1857  			bav._setPKIDMappings(pkidEntry)
  1858  
  1859  			// Set nil profile entries
  1860  			bav.ProfilePKIDToProfileEntry[*pkidEntry.PKID] = nil
  1861  		}
  1862  
  1863  		// Set real entries for all the profiles that actually exist
  1864  		result := bav.Postgres.GetProfilesForPublicKeys(publicKeys)
  1865  		for _, profile := range result {
  1866  			bav.setProfileMappings(profile)
  1867  		}
  1868  	}
  1869  
  1870  	// One iteration for everything else
  1871  	// TODO: For some reason just fetching follows from the DB causes consensus issues??
  1872  	var outputs []*PGTransactionOutput
  1873  	var follows []*PGFollow
  1874  	var balances []*PGCreatorCoinBalance
  1875  	var likes []*PGLike
  1876  	var posts []*PGPost
  1877  	var lowercaseUsernames []string
  1878  
  1879  	for _, txn := range desoBlock.Txns {
  1880  		// Preload all the inputs
  1881  		for _, txInput := range txn.TxInputs {
  1882  			output := &PGTransactionOutput{
  1883  				OutputHash:  &txInput.TxID,
  1884  				OutputIndex: txInput.Index,
  1885  				Spent:       false,
  1886  			}
  1887  			outputs = append(outputs, output)
  1888  		}
  1889  
  1890  		if txn.TxnMeta.GetTxnType() == TxnTypeFollow {
  1891  			txnMeta := txn.TxnMeta.(*FollowMetadata)
  1892  			follow := &PGFollow{
  1893  				FollowerPKID: bav.GetPKIDForPublicKey(txn.PublicKey).PKID.NewPKID(),
  1894  				FollowedPKID: bav.GetPKIDForPublicKey(txnMeta.FollowedPublicKey).PKID.NewPKID(),
  1895  			}
  1896  			follows = append(follows, follow)
  1897  
  1898  			// We cache the follow as not present and then fill them in later
  1899  			followerKey := MakeFollowKey(follow.FollowerPKID, follow.FollowedPKID)
  1900  			bav.FollowKeyToFollowEntry[followerKey] = nil
  1901  		} else if txn.TxnMeta.GetTxnType() == TxnTypeCreatorCoin {
  1902  			txnMeta := txn.TxnMeta.(*CreatorCoinMetadataa)
  1903  
  1904  			// Fetch the buyer's balance entry
  1905  			balance := &PGCreatorCoinBalance{
  1906  				HolderPKID:  bav.GetPKIDForPublicKey(txn.PublicKey).PKID.NewPKID(),
  1907  				CreatorPKID: bav.GetPKIDForPublicKey(txnMeta.ProfilePublicKey).PKID.NewPKID(),
  1908  			}
  1909  			balances = append(balances, balance)
  1910  
  1911  			// We cache the balances as not present and then fill them in later
  1912  			balanceEntryKey := MakeCreatorCoinBalanceKey(balance.HolderPKID, balance.CreatorPKID)
  1913  			bav.HODLerPKIDCreatorPKIDToBalanceEntry[balanceEntryKey] = nil
  1914  
  1915  			// Fetch the creator's balance entry if they're not buying their own coin
  1916  			if !reflect.DeepEqual(txn.PublicKey, txnMeta.ProfilePublicKey) {
  1917  				balance = &PGCreatorCoinBalance{
  1918  					HolderPKID:  bav.GetPKIDForPublicKey(txnMeta.ProfilePublicKey).PKID.NewPKID(),
  1919  					CreatorPKID: bav.GetPKIDForPublicKey(txnMeta.ProfilePublicKey).PKID.NewPKID(),
  1920  				}
  1921  				balances = append(balances, balance)
  1922  
  1923  				// We cache the balances as not present and then fill them in later
  1924  				balanceEntryKey = MakeCreatorCoinBalanceKey(balance.HolderPKID, balance.CreatorPKID)
  1925  				bav.HODLerPKIDCreatorPKIDToBalanceEntry[balanceEntryKey] = nil
  1926  			}
  1927  		} else if txn.TxnMeta.GetTxnType() == TxnTypeLike {
  1928  			txnMeta := txn.TxnMeta.(*LikeMetadata)
  1929  			like := &PGLike{
  1930  				LikerPublicKey: txn.PublicKey,
  1931  				LikedPostHash:  txnMeta.LikedPostHash.NewBlockHash(),
  1932  			}
  1933  			likes = append(likes, like)
  1934  
  1935  			// We cache the likes as not present and then fill them in later
  1936  			likeKey := MakeLikeKey(like.LikerPublicKey, *like.LikedPostHash)
  1937  			bav.LikeKeyToLikeEntry[likeKey] = nil
  1938  
  1939  			post := &PGPost{
  1940  				PostHash: txnMeta.LikedPostHash.NewBlockHash(),
  1941  			}
  1942  			posts = append(posts, post)
  1943  
  1944  			// We cache the posts as not present and then fill them in later
  1945  			bav.PostHashToPostEntry[*post.PostHash] = nil
  1946  		} else if txn.TxnMeta.GetTxnType() == TxnTypeSubmitPost {
  1947  			txnMeta := txn.TxnMeta.(*SubmitPostMetadata)
  1948  
  1949  			var postHash *BlockHash
  1950  			if len(txnMeta.PostHashToModify) != 0 {
  1951  				postHash = NewBlockHash(txnMeta.PostHashToModify)
  1952  			} else {
  1953  				postHash = txn.Hash()
  1954  			}
  1955  
  1956  			posts = append(posts, &PGPost{
  1957  				PostHash: postHash,
  1958  			})
  1959  
  1960  			// We cache the posts as not present and then fill them in later
  1961  			bav.PostHashToPostEntry[*postHash] = nil
  1962  
  1963  			// TODO: Preload parent, grandparent, and reposted posts
  1964  		} else if txn.TxnMeta.GetTxnType() == TxnTypeUpdateProfile {
  1965  			txnMeta := txn.TxnMeta.(*UpdateProfileMetadata)
  1966  			if len(txnMeta.NewUsername) == 0 {
  1967  				continue
  1968  			}
  1969  
  1970  			lowercaseUsernames = append(lowercaseUsernames, strings.ToLower(string(txnMeta.NewUsername)))
  1971  
  1972  			// We cache the profiles as not present and then fill them in later
  1973  			bav.ProfileUsernameToProfileEntry[MakeUsernameMapKey(txnMeta.NewUsername)] = nil
  1974  		}
  1975  	}
  1976  
  1977  	if len(outputs) > 0 {
  1978  		//foundOutputs := bav.Postgres.GetOutputs(outputs)
  1979  		//for _, output := range foundOutputs {
  1980  		//	err := bav._setUtxoMappings(output.NewUtxoEntry())
  1981  		//	if err != nil {
  1982  		//		return err
  1983  		//	}
  1984  		//}
  1985  	}
  1986  
  1987  	if len(follows) > 0 {
  1988  		foundFollows := bav.Postgres.GetFollows(follows)
  1989  		for _, follow := range foundFollows {
  1990  			followEntry := follow.NewFollowEntry()
  1991  			bav._setFollowEntryMappings(followEntry)
  1992  		}
  1993  	}
  1994  
  1995  	if len(balances) > 0 {
  1996  		foundBalances := bav.Postgres.GetCreatorCoinBalances(balances)
  1997  		for _, balance := range foundBalances {
  1998  			balanceEntry := balance.NewBalanceEntry()
  1999  			bav._setBalanceEntryMappings(balanceEntry)
  2000  		}
  2001  	}
  2002  
  2003  	if len(likes) > 0 {
  2004  		foundLikes := bav.Postgres.GetLikes(likes)
  2005  		for _, like := range foundLikes {
  2006  			likeEntry := like.NewLikeEntry()
  2007  			bav._setLikeEntryMappings(likeEntry)
  2008  		}
  2009  	}
  2010  
  2011  	if len(posts) > 0 {
  2012  		foundPosts := bav.Postgres.GetPosts(posts)
  2013  		for _, post := range foundPosts {
  2014  			bav.setPostMappings(post)
  2015  		}
  2016  	}
  2017  
  2018  	if len(lowercaseUsernames) > 0 {
  2019  		foundProfiles := bav.Postgres.GetProfilesForUsername(lowercaseUsernames)
  2020  		for _, profile := range foundProfiles {
  2021  			bav.setProfileMappings(profile)
  2022  		}
  2023  	}
  2024  
  2025  	return nil
  2026  }
  2027  
  2028  // GetUnspentUtxoEntrysForPublicKey returns the UtxoEntrys corresponding to the
  2029  // passed-in public key that are currently unspent. It does this while factoring
  2030  // in any transactions that have already been connected to it. This is useful,
  2031  // as an example, when one whats to see what UtxoEntrys are available for spending
  2032  // after factoring in (i.e. connecting) all of the transactions currently in the
  2033  // mempool that are related to this public key.
  2034  //
  2035  // At a high level, this function allows one to get the utxos that are the union of:
  2036  // - utxos in the db
  2037  // - utxos in the view from previously-connected transactions
  2038  func (bav *UtxoView) GetUnspentUtxoEntrysForPublicKey(pkBytes []byte) ([]*UtxoEntry, error) {
  2039  	// Fetch the relevant utxos for this public key from the db. We do this because
  2040  	// the db could contain utxos that are not currently loaded into the view.
  2041  	var utxoEntriesForPublicKey []*UtxoEntry
  2042  	var err error
  2043  	if bav.Postgres != nil {
  2044  		utxoEntriesForPublicKey = bav.Postgres.GetUtxoEntriesForPublicKey(pkBytes)
  2045  	} else {
  2046  		utxoEntriesForPublicKey, err = DbGetUtxosForPubKey(pkBytes, bav.Handle)
  2047  	}
  2048  	if err != nil {
  2049  		return nil, errors.Wrapf(err, "UtxoView.GetUnspentUtxoEntrysForPublicKey: Problem fetching "+
  2050  			"utxos for public key %s", PkToString(pkBytes, bav.Params))
  2051  	}
  2052  
  2053  	// Load all the utxos associated with this public key into
  2054  	// the view. This makes it so that the view can enumerate all of the utxoEntries
  2055  	// known for this public key. To put it another way, it allows the view to
  2056  	// contain the union of:
  2057  	// - utxos in the db
  2058  	// - utxos in the view from previously-connected transactions
  2059  	for _, utxoEntry := range utxoEntriesForPublicKey {
  2060  		bav.GetUtxoEntryForUtxoKey(utxoEntry.UtxoKey)
  2061  	}
  2062  
  2063  	// Now that all of the utxos for this key have been loaded, filter the
  2064  	// ones for this public key and return them.
  2065  	utxoEntriesToReturn := []*UtxoEntry{}
  2066  	for utxoKeyTmp, utxoEntry := range bav.UtxoKeyToUtxoEntry {
  2067  		// Make a copy of the iterator since it might change from underneath us
  2068  		// if we take its pointer.
  2069  		utxoKey := utxoKeyTmp
  2070  		utxoEntry.UtxoKey = &utxoKey
  2071  		if !utxoEntry.isSpent && reflect.DeepEqual(utxoEntry.PublicKey, pkBytes) {
  2072  			utxoEntriesToReturn = append(utxoEntriesToReturn, utxoEntry)
  2073  		}
  2074  	}
  2075  
  2076  	return utxoEntriesToReturn, nil
  2077  }
  2078  
  2079  func (bav *UtxoView) GetSpendableDeSoBalanceNanosForPublicKey(pkBytes []byte,
  2080  	tipHeight uint32) (_spendableBalance uint64, _err error) {
  2081  	// In order to get the spendable balance, we need to account for any immature block rewards.
  2082  	// We get these by starting at the chain tip and iterating backwards until we have collected
  2083  	// all of the immature block rewards for this public key.
  2084  	nextBlockHash := bav.TipHash
  2085  	numImmatureBlocks := uint32(bav.Params.BlockRewardMaturity / bav.Params.TimeBetweenBlocks)
  2086  	immatureBlockRewards := uint64(0)
  2087  
  2088  	if bav.Postgres != nil {
  2089  		// TODO: Filter out immature block rewards in postgres. UtxoType needs to be set correctly when importing blocks
  2090  		//outputs := bav.Postgres.GetBlockRewardsForPublicKey(NewPublicKey(pkBytes), tipHeight-numImmatureBlocks, tipHeight)
  2091  		//for _, output := range outputs {
  2092  		//	immatureBlockRewards += output.AmountNanos
  2093  		//}
  2094  	} else {
  2095  		for ii := uint64(1); ii < uint64(numImmatureBlocks); ii++ {
  2096  			// Don't look up the genesis block since it isn't in the DB.
  2097  			if GenesisBlockHashHex == nextBlockHash.String() {
  2098  				break
  2099  			}
  2100  
  2101  			blockNode := GetHeightHashToNodeInfo(bav.Handle, tipHeight, nextBlockHash, false)
  2102  			if blockNode == nil {
  2103  				return uint64(0), fmt.Errorf(
  2104  					"GetSpendableDeSoBalanceNanosForPublicKey: Problem getting block for blockhash %s",
  2105  					nextBlockHash.String())
  2106  			}
  2107  			blockRewardForPK, err := DbGetBlockRewardForPublicKeyBlockHash(bav.Handle, pkBytes, nextBlockHash)
  2108  			if err != nil {
  2109  				return uint64(0), errors.Wrapf(
  2110  					err, "GetSpendableDeSoBalanceNanosForPublicKey: Problem getting block reward for "+
  2111  						"public key %s blockhash %s", PkToString(pkBytes, bav.Params), nextBlockHash.String())
  2112  			}
  2113  			immatureBlockRewards += blockRewardForPK
  2114  			if blockNode.Parent != nil {
  2115  				nextBlockHash = blockNode.Parent.Hash
  2116  			} else {
  2117  				nextBlockHash = GenesisBlockHash
  2118  			}
  2119  		}
  2120  	}
  2121  
  2122  	balanceNanos, err := bav.GetDeSoBalanceNanosForPublicKey(pkBytes)
  2123  	if err != nil {
  2124  		return uint64(0), errors.Wrap(err, "GetSpendableUtxosForPublicKey: ")
  2125  	}
  2126  	// Sanity check that the balanceNanos >= immatureBlockRewards to prevent underflow.
  2127  	if balanceNanos < immatureBlockRewards {
  2128  		return uint64(0), fmt.Errorf(
  2129  			"GetSpendableUtxosForPublicKey: balance underflow (%d,%d)", balanceNanos, immatureBlockRewards)
  2130  	}
  2131  	return balanceNanos - immatureBlockRewards, nil
  2132  }