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

     1  package lib
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/dgraph-io/badger/v3"
     6  	"github.com/stretchr/testify/assert"
     7  	"github.com/stretchr/testify/require"
     8  	"testing"
     9  )
    10  
    11  func _doLikeTxn(t *testing.T, chain *Blockchain, db *badger.DB,
    12  	params *DeSoParams, feeRateNanosPerKB uint64, senderPkBase58Check string,
    13  	likedPostHash BlockHash, senderPrivBase58Check string, isUnfollow bool) (
    14  	_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
    15  
    16  	assert := assert.New(t)
    17  	require := require.New(t)
    18  	_ = assert
    19  	_ = require
    20  
    21  	senderPkBytes, _, err := Base58CheckDecode(senderPkBase58Check)
    22  	require.NoError(err)
    23  
    24  	utxoView, err := NewUtxoView(db, params, nil)
    25  	require.NoError(err)
    26  
    27  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateLikeTxn(
    28  		senderPkBytes, likedPostHash, isUnfollow, feeRateNanosPerKB, nil, []*DeSoOutput{})
    29  	if err != nil {
    30  		return nil, nil, 0, err
    31  	}
    32  
    33  	require.Equal(totalInputMake, changeAmountMake+feesMake)
    34  
    35  	// Sign the transaction now that its inputs are set up.
    36  	_signTxn(t, txn, senderPrivBase58Check)
    37  
    38  	txHash := txn.Hash()
    39  	// Always use height+1 for validation since it's assumed the transaction will
    40  	// get mined into the next block.
    41  	blockHeight := chain.blockTip().Height + 1
    42  	utxoOps, totalInput, totalOutput, fees, err :=
    43  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true, /*verifySignature*/
    44  			false /*ignoreUtxos*/)
    45  	// ConnectTransaction should treat the amount locked as contributing to the
    46  	// output.
    47  	if err != nil {
    48  		return nil, nil, 0, err
    49  	}
    50  	require.Equal(totalInput, totalOutput+fees)
    51  	require.Equal(totalInput, totalInputMake)
    52  
    53  	// We should have one SPEND UtxoOperation for each input, one ADD operation
    54  	// for each output, and one OperationTypeLike operation at the end.
    55  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
    56  	for ii := 0; ii < len(txn.TxInputs); ii++ {
    57  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
    58  	}
    59  	require.Equal(OperationTypeLike, utxoOps[len(utxoOps)-1].Type)
    60  
    61  	require.NoError(utxoView.FlushToDb())
    62  
    63  	return utxoOps, txn, blockHeight, nil
    64  }
    65  
    66  func TestLikeTxns(t *testing.T) {
    67  	assert := assert.New(t)
    68  	require := require.New(t)
    69  	_ = assert
    70  	_ = require
    71  
    72  	chain, params, db := NewLowDifficultyBlockchain()
    73  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
    74  
    75  	// Mine a few blocks to give the senderPkString some money.
    76  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
    77  	require.NoError(err)
    78  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
    79  	require.NoError(err)
    80  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
    81  	require.NoError(err)
    82  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
    83  	require.NoError(err)
    84  
    85  	// Setup some convenience functions for the test.
    86  	txnOps := [][]*UtxoOperation{}
    87  	txns := []*MsgDeSoTxn{}
    88  	expectedSenderBalances := []uint64{}
    89  	expectedRecipientBalances := []uint64{}
    90  
    91  	// We take the block tip to be the blockchain height rather than the
    92  	// header chain height.
    93  	savedHeight := chain.blockTip().Height + 1
    94  	registerOrTransfer := func(username string,
    95  		senderPk string, recipientPk string, senderPriv string) {
    96  
    97  		expectedSenderBalances = append(
    98  			expectedSenderBalances, _getBalance(t, chain, nil, senderPkString))
    99  		expectedRecipientBalances = append(
   100  			expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString))
   101  
   102  		currentOps, currentTxn, _ := _doBasicTransferWithViewFlush(
   103  			t, chain, db, params, senderPk, recipientPk,
   104  			senderPriv, 7 /*amount to send*/, 11 /*feerate*/)
   105  
   106  		txnOps = append(txnOps, currentOps)
   107  		txns = append(txns, currentTxn)
   108  	}
   109  
   110  	// Fund all the keys.
   111  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   112  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   113  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   114  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   115  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   116  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   117  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   118  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   119  	registerOrTransfer("", senderPkString, m2Pub, senderPrivString)
   120  	registerOrTransfer("", senderPkString, m2Pub, senderPrivString)
   121  	registerOrTransfer("", senderPkString, m2Pub, senderPrivString)
   122  	registerOrTransfer("", senderPkString, m2Pub, senderPrivString)
   123  	registerOrTransfer("", senderPkString, m3Pub, senderPrivString)
   124  	registerOrTransfer("", senderPkString, m3Pub, senderPrivString)
   125  	registerOrTransfer("", senderPkString, m3Pub, senderPrivString)
   126  	registerOrTransfer("", senderPkString, m3Pub, senderPrivString)
   127  
   128  	doLikeTxn := func(
   129  		senderPkBase58Check string, likedPostHash BlockHash,
   130  		senderPrivBase58Check string, isUnfollow bool, feeRateNanosPerKB uint64) {
   131  
   132  		expectedSenderBalances = append(
   133  			expectedSenderBalances, _getBalance(t, chain, nil, senderPkString))
   134  		expectedRecipientBalances = append(
   135  			expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString))
   136  
   137  		currentOps, currentTxn, _, err := _doLikeTxn(
   138  			t, chain, db, params, feeRateNanosPerKB, senderPkBase58Check,
   139  			likedPostHash, senderPrivBase58Check, isUnfollow)
   140  		require.NoError(err)
   141  
   142  		txnOps = append(txnOps, currentOps)
   143  		txns = append(txns, currentTxn)
   144  	}
   145  
   146  	submitPost := func(
   147  		feeRateNanosPerKB uint64, updaterPkBase58Check string,
   148  		updaterPrivBase58Check string,
   149  		postHashToModify []byte,
   150  		parentStakeID []byte,
   151  		bodyObj *DeSoBodySchema,
   152  		repostedPostHash []byte,
   153  		tstampNanos uint64,
   154  		isHidden bool) {
   155  
   156  		expectedSenderBalances = append(
   157  			expectedSenderBalances, _getBalance(t, chain, nil, senderPkString))
   158  		expectedRecipientBalances = append(
   159  			expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString))
   160  
   161  		currentOps, currentTxn, _, err := _submitPost(
   162  			t, chain, db, params, feeRateNanosPerKB,
   163  			updaterPkBase58Check,
   164  			updaterPrivBase58Check,
   165  			postHashToModify,
   166  			parentStakeID,
   167  			bodyObj,
   168  			repostedPostHash,
   169  			tstampNanos,
   170  			isHidden)
   171  
   172  		require.NoError(err)
   173  
   174  		txnOps = append(txnOps, currentOps)
   175  		txns = append(txns, currentTxn)
   176  	}
   177  
   178  	fakePostHash := BlockHash{
   179  		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
   180  		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
   181  		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
   182  		0x30, 0x31,
   183  	}
   184  	// Attempting "m0 -> fakePostHash" should fail since the post doesn't exist.
   185  	_, _, _, err = _doLikeTxn(
   186  		t, chain, db, params, 10 /*feeRateNanosPerKB*/, m0Pub,
   187  		fakePostHash, m0Priv, false /*isUnfollow*/)
   188  	require.Error(err)
   189  	require.Contains(err.Error(), RuleErrorCannotLikeNonexistentPost)
   190  
   191  	submitPost(
   192  		10,       /*feeRateNanosPerKB*/
   193  		m0Pub,    /*updaterPkBase58Check*/
   194  		m0Priv,   /*updaterPrivBase58Check*/
   195  		[]byte{}, /*postHashToModify*/
   196  		[]byte{}, /*parentStakeID*/
   197  		&DeSoBodySchema{Body: "m0 post body 1 no profile"}, /*body*/
   198  		[]byte{},
   199  		1602947011*1e9, /*tstampNanos*/
   200  		false /*isHidden*/)
   201  	post1Txn := txns[len(txns)-1]
   202  	post1Hash := *post1Txn.Hash()
   203  
   204  	{
   205  		submitPost(
   206  			10,       /*feeRateNanosPerKB*/
   207  			m0Pub,    /*updaterPkBase58Check*/
   208  			m0Priv,   /*updaterPrivBase58Check*/
   209  			[]byte{}, /*postHashToModify*/
   210  			[]byte{}, /*parentStakeID*/
   211  			&DeSoBodySchema{Body: "m0 post body 2 no profile"}, /*body*/
   212  			[]byte{},
   213  			1502947012*1e9, /*tstampNanos*/
   214  			false /*isHidden*/)
   215  	}
   216  	post2Txn := txns[len(txns)-1]
   217  	post2Hash := *post2Txn.Hash()
   218  
   219  	{
   220  		submitPost(
   221  			10,       /*feeRateNanosPerKB*/
   222  			m1Pub,    /*updaterPkBase58Check*/
   223  			m1Priv,   /*updaterPrivBase58Check*/
   224  			[]byte{}, /*postHashToModify*/
   225  			[]byte{}, /*parentStakeID*/
   226  			&DeSoBodySchema{Body: "m1 post body 1 no profile"}, /*body*/
   227  			[]byte{},
   228  			1502947013*1e9, /*tstampNanos*/
   229  			false /*isHidden*/)
   230  	}
   231  	post3Txn := txns[len(txns)-1]
   232  	post3Hash := *post3Txn.Hash()
   233  
   234  	// m0 -> p1
   235  	doLikeTxn(m0Pub, post1Hash, m0Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/)
   236  
   237  	// Duplicating "m0 -> p1" should fail.
   238  	_, _, _, err = _doLikeTxn(
   239  		t, chain, db, params, 10 /*feeRateNanosPerKB*/, m0Pub,
   240  		post1Hash, m0Priv, false /*isUnfollow*/)
   241  	require.Error(err)
   242  	require.Contains(err.Error(), RuleErrorLikeEntryAlreadyExists)
   243  
   244  	// m2 -> p1
   245  	doLikeTxn(m2Pub, post1Hash, m2Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/)
   246  
   247  	// m3 -> p1
   248  	doLikeTxn(m3Pub, post1Hash, m3Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/)
   249  
   250  	// m3 -> p2
   251  	doLikeTxn(m3Pub, post2Hash, m3Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/)
   252  
   253  	// m1 -> p2
   254  	doLikeTxn(m1Pub, post2Hash, m1Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/)
   255  
   256  	// m2 -> p3
   257  	doLikeTxn(m2Pub, post3Hash, m2Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/)
   258  
   259  	likingP1 := [][]byte{
   260  		_strToPk(t, m0Pub),
   261  		_strToPk(t, m2Pub),
   262  		_strToPk(t, m3Pub),
   263  	}
   264  
   265  	likingP2 := [][]byte{
   266  		_strToPk(t, m1Pub),
   267  		_strToPk(t, m3Pub),
   268  	}
   269  
   270  	likingP3 := [][]byte{
   271  		_strToPk(t, m2Pub),
   272  	}
   273  
   274  	// Verify pks liking p1 and check like count.
   275  	{
   276  		likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post1Hash)
   277  		require.NoError(err)
   278  		require.Equal(len(likingP1), len(likingPks))
   279  		for ii := 0; ii < len(likingPks); ii++ {
   280  			require.Contains(likingP1, likingPks[ii])
   281  		}
   282  		post1 := DBGetPostEntryByPostHash(db, &post1Hash)
   283  		require.Equal(uint64(len(likingP1)), post1.LikeCount)
   284  	}
   285  
   286  	// Verify pks liking p2 and check like count.
   287  	{
   288  		likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post2Hash)
   289  		require.NoError(err)
   290  		require.Equal(len(likingP2), len(likingPks))
   291  		for ii := 0; ii < len(likingPks); ii++ {
   292  			require.Contains(likingP2, likingPks[ii])
   293  		}
   294  		post2 := DBGetPostEntryByPostHash(db, &post2Hash)
   295  		require.Equal(uint64(len(likingP2)), post2.LikeCount)
   296  	}
   297  
   298  	// Verify pks liking p3 and check like count.
   299  	{
   300  		likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post3Hash)
   301  		require.NoError(err)
   302  		require.Equal(len(likingP3), len(likingPks))
   303  		for ii := 0; ii < len(likingPks); ii++ {
   304  			require.Contains(likingP3, likingPks[ii])
   305  		}
   306  		post3 := DBGetPostEntryByPostHash(db, &post3Hash)
   307  		require.Equal(uint64(len(likingP3)), post3.LikeCount)
   308  	}
   309  
   310  	m0Likes := []BlockHash{
   311  		post1Hash,
   312  	}
   313  
   314  	m1Likes := []BlockHash{
   315  		post2Hash,
   316  	}
   317  
   318  	m2Likes := []BlockHash{
   319  		post1Hash,
   320  		post3Hash,
   321  	}
   322  
   323  	m3Likes := []BlockHash{
   324  		post1Hash,
   325  		post2Hash,
   326  	}
   327  
   328  	// Verify m0's likes.
   329  	{
   330  		likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m0Pub))
   331  		require.NoError(err)
   332  		require.Equal(len(m0Likes), len(likedPostHashes))
   333  		for ii := 0; ii < len(likedPostHashes); ii++ {
   334  			require.Contains(m0Likes, *likedPostHashes[ii])
   335  		}
   336  	}
   337  
   338  	// Verify m1's likes.
   339  	{
   340  		likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m1Pub))
   341  		require.NoError(err)
   342  		require.Equal(len(m1Likes), len(likedPostHashes))
   343  		for ii := 0; ii < len(likedPostHashes); ii++ {
   344  			require.Contains(m1Likes, *likedPostHashes[ii])
   345  		}
   346  	}
   347  
   348  	// Verify m2's likes.
   349  	{
   350  		likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m2Pub))
   351  		require.NoError(err)
   352  		require.Equal(len(m2Likes), len(likedPostHashes))
   353  		for ii := 0; ii < len(likedPostHashes); ii++ {
   354  			require.Contains(m2Likes, *likedPostHashes[ii])
   355  		}
   356  	}
   357  
   358  	// Verify m3's likes.
   359  	{
   360  		likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m3Pub))
   361  		require.NoError(err)
   362  		require.Equal(len(m3Likes), len(likedPostHashes))
   363  		for ii := 0; ii < len(likedPostHashes); ii++ {
   364  			require.Contains(m3Likes, *likedPostHashes[ii])
   365  		}
   366  	}
   367  
   368  	// Try an "unlike."
   369  	//
   370  	// m0 -> p1 (unfollow)
   371  	doLikeTxn(m0Pub, post1Hash, m0Priv, true /*isUnfollow*/, 10 /*feeRateNanosPerKB*/)
   372  
   373  	// m3 -> p2 (unfollow)
   374  	doLikeTxn(m3Pub, post2Hash, m3Priv, true /*isUnfollow*/, 10 /*feeRateNanosPerKB*/)
   375  
   376  	// Duplicating "m0 -> p1" (unfollow) should fail.
   377  	_, _, _, err = _doLikeTxn(
   378  		t, chain, db, params, 10 /*feeRateNanosPerKB*/, m0Pub,
   379  		post1Hash, m0Priv, true /*isUnfollow*/)
   380  	require.Error(err)
   381  	require.Contains(err.Error(), RuleErrorCannotUnlikeWithoutAnExistingLike)
   382  
   383  	likingP1 = [][]byte{
   384  		_strToPk(t, m2Pub),
   385  		_strToPk(t, m3Pub),
   386  	}
   387  
   388  	likingP2 = [][]byte{
   389  		_strToPk(t, m1Pub),
   390  	}
   391  
   392  	// Verify pks liking p1 and check like count.
   393  	{
   394  		likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post1Hash)
   395  		require.NoError(err)
   396  		require.Equal(len(likingP1), len(likingPks))
   397  		for ii := 0; ii < len(likingPks); ii++ {
   398  			require.Contains(likingP1, likingPks[ii])
   399  		}
   400  		post1 := DBGetPostEntryByPostHash(db, &post1Hash)
   401  		require.Equal(uint64(len(likingP1)), post1.LikeCount)
   402  	}
   403  
   404  	// Verify pks liking p2 and check like count.
   405  	{
   406  		likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post2Hash)
   407  		require.NoError(err)
   408  		require.Equal(len(likingP2), len(likingPks))
   409  		for ii := 0; ii < len(likingPks); ii++ {
   410  			require.Contains(likingP2, likingPks[ii])
   411  		}
   412  		post2 := DBGetPostEntryByPostHash(db, &post2Hash)
   413  		require.Equal(uint64(len(likingP2)), post2.LikeCount)
   414  	}
   415  
   416  	m3Likes = []BlockHash{
   417  		post1Hash,
   418  	}
   419  
   420  	// Verify m0 has no likes.
   421  	{
   422  		likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m0Pub))
   423  		require.NoError(err)
   424  		require.Equal(0, len(likedPostHashes))
   425  	}
   426  
   427  	// Verify m3's likes.
   428  	{
   429  		likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m3Pub))
   430  		require.NoError(err)
   431  		require.Equal(len(m3Likes), len(likedPostHashes))
   432  		for i := 0; i < len(likedPostHashes); i++ {
   433  			require.Contains(m3Likes, *likedPostHashes[i])
   434  		}
   435  	}
   436  
   437  	// ===================================================================================
   438  	// Finish it off with some transactions
   439  	// ===================================================================================
   440  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   441  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   442  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   443  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   444  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   445  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   446  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   447  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   448  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   449  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   450  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   451  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   452  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   453  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   454  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   455  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   456  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   457  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   458  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   459  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   460  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   461  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   462  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   463  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   464  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   465  
   466  	// Roll back all of the above using the utxoOps from each.
   467  	for ii := 0; ii < len(txnOps); ii++ {
   468  		backwardIter := len(txnOps) - 1 - ii
   469  		currentOps := txnOps[backwardIter]
   470  		currentTxn := txns[backwardIter]
   471  		fmt.Printf(
   472  			"Disconnecting transaction with type %v index %d (going backwards)\n",
   473  			currentTxn.TxnMeta.GetTxnType(), backwardIter)
   474  
   475  		utxoView, err := NewUtxoView(db, params, nil)
   476  		require.NoError(err)
   477  
   478  		currentHash := currentTxn.Hash()
   479  		err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight)
   480  		require.NoError(err)
   481  
   482  		require.NoError(utxoView.FlushToDb())
   483  
   484  		// After disconnecting, the balances should be restored to what they
   485  		// were before this transaction was applied.
   486  		require.Equal(
   487  			int64(expectedSenderBalances[backwardIter]),
   488  			int64(_getBalance(t, chain, nil, senderPkString)))
   489  		require.Equal(
   490  			expectedRecipientBalances[backwardIter],
   491  			_getBalance(t, chain, nil, recipientPkString))
   492  
   493  		// Here we check the like counts after all the like entries have been disconnected.
   494  		if backwardIter == 19 {
   495  			post1 := DBGetPostEntryByPostHash(db, &post1Hash)
   496  			require.Equal(uint64(0), post1.LikeCount)
   497  			post2 := DBGetPostEntryByPostHash(db, &post2Hash)
   498  			require.Equal(uint64(0), post2.LikeCount)
   499  			post3 := DBGetPostEntryByPostHash(db, &post3Hash)
   500  			require.Equal(uint64(0), post3.LikeCount)
   501  		}
   502  	}
   503  
   504  	testDisconnectedState := func() {
   505  		// Verify that all the pks liking each post hash have been deleted and like count == 0.
   506  		{
   507  			likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post1Hash)
   508  			require.NoError(err)
   509  			require.Equal(0, len(likingPks))
   510  		}
   511  		{
   512  			likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post2Hash)
   513  			require.NoError(err)
   514  			require.Equal(0, len(likingPks))
   515  		}
   516  		{
   517  			likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post3Hash)
   518  			require.NoError(err)
   519  			require.Equal(0, len(likingPks))
   520  		}
   521  
   522  		// Verify that all the post hashes liked by users have been deleted.
   523  		{
   524  			likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m0Pub))
   525  			require.NoError(err)
   526  			require.Equal(0, len(likedPostHashes))
   527  		}
   528  		{
   529  			likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m1Pub))
   530  			require.NoError(err)
   531  			require.Equal(0, len(likedPostHashes))
   532  		}
   533  		{
   534  			likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m2Pub))
   535  			require.NoError(err)
   536  			require.Equal(0, len(likedPostHashes))
   537  		}
   538  		{
   539  			likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m3Pub))
   540  			require.NoError(err)
   541  			require.Equal(0, len(likedPostHashes))
   542  		}
   543  	}
   544  	testDisconnectedState()
   545  
   546  	// Apply all the transactions to a mempool object and make sure we don't get any
   547  	// errors. Verify the balances align as we go.
   548  	for ii, tx := range txns {
   549  		// See comment above on this transaction.
   550  		fmt.Printf("Adding txn %d of type %v to mempool\n", ii, tx.TxnMeta.GetTxnType())
   551  
   552  		require.Equal(expectedSenderBalances[ii], _getBalance(t, chain, mempool, senderPkString))
   553  		require.Equal(expectedRecipientBalances[ii], _getBalance(t, chain, mempool, recipientPkString))
   554  
   555  		_, err := mempool.ProcessTransaction(tx, false, false, 0, true)
   556  		require.NoError(err, "Problem adding transaction %d to mempool: %v", ii, tx)
   557  	}
   558  
   559  	// Apply all the transactions to a view and flush the view to the db.
   560  	utxoView, err := NewUtxoView(db, params, nil)
   561  	require.NoError(err)
   562  	for ii, txn := range txns {
   563  		fmt.Printf("Adding txn %v of type %v to UtxoView\n", ii, txn.TxnMeta.GetTxnType())
   564  
   565  		// Always use height+1 for validation since it's assumed the transaction will
   566  		// get mined into the next block.
   567  		txHash := txn.Hash()
   568  		blockHeight := chain.blockTip().Height + 1
   569  		_, _, _, _, err :=
   570  			utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   571  		require.NoError(err)
   572  	}
   573  	// Flush the utxoView after having added all the transactions.
   574  	require.NoError(utxoView.FlushToDb())
   575  
   576  	testConnectedState := func() {
   577  		likingP1 = [][]byte{
   578  			_strToPk(t, m2Pub),
   579  			_strToPk(t, m3Pub),
   580  		}
   581  
   582  		likingP2 = [][]byte{
   583  			_strToPk(t, m1Pub),
   584  		}
   585  
   586  		likingP3 := [][]byte{
   587  			_strToPk(t, m2Pub),
   588  		}
   589  
   590  		// Verify pks liking p1 and check like count.
   591  		{
   592  			likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post1Hash)
   593  			require.NoError(err)
   594  			require.Equal(len(likingP1), len(likingPks))
   595  			for ii := 0; ii < len(likingPks); ii++ {
   596  				require.Contains(likingP1, likingPks[ii])
   597  			}
   598  			post1 := DBGetPostEntryByPostHash(db, &post1Hash)
   599  			require.Equal(uint64(len(likingP1)), post1.LikeCount)
   600  		}
   601  
   602  		// Verify pks liking p2 and check like count.
   603  		{
   604  			likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post2Hash)
   605  			require.NoError(err)
   606  			require.Equal(len(likingP2), len(likingPks))
   607  			for ii := 0; ii < len(likingPks); ii++ {
   608  				require.Contains(likingP2, likingPks[ii])
   609  			}
   610  			post2 := DBGetPostEntryByPostHash(db, &post2Hash)
   611  			require.Equal(uint64(len(likingP2)), post2.LikeCount)
   612  		}
   613  
   614  		// Verify pks liking p3 and check like count.
   615  		{
   616  			likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post3Hash)
   617  			require.NoError(err)
   618  			require.Equal(len(likingP3), len(likingPks))
   619  			for ii := 0; ii < len(likingPks); ii++ {
   620  				require.Contains(likingP3, likingPks[ii])
   621  			}
   622  			post3 := DBGetPostEntryByPostHash(db, &post3Hash)
   623  			require.Equal(uint64(len(likingP3)), post3.LikeCount)
   624  		}
   625  
   626  		m1Likes := []BlockHash{
   627  			post2Hash,
   628  		}
   629  
   630  		m2Likes := []BlockHash{
   631  			post1Hash,
   632  			post3Hash,
   633  		}
   634  
   635  		m3Likes = []BlockHash{
   636  			post1Hash,
   637  		}
   638  
   639  		// Verify m0 has no likes.
   640  		{
   641  			followPks, err := DbGetPostHashesYouLike(db, _strToPk(t, m0Pub))
   642  			require.NoError(err)
   643  			require.Equal(0, len(followPks))
   644  		}
   645  
   646  		// Verify m1's likes.
   647  		{
   648  			likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m1Pub))
   649  			require.NoError(err)
   650  			require.Equal(len(m1Likes), len(likedPostHashes))
   651  			for ii := 0; ii < len(likedPostHashes); ii++ {
   652  				require.Contains(m1Likes, *likedPostHashes[ii])
   653  			}
   654  		}
   655  
   656  		// Verify m2's likes.
   657  		{
   658  			likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m2Pub))
   659  			require.NoError(err)
   660  			require.Equal(len(m2Likes), len(likedPostHashes))
   661  			for ii := 0; ii < len(likedPostHashes); ii++ {
   662  				require.Contains(m2Likes, *likedPostHashes[ii])
   663  			}
   664  		}
   665  
   666  		// Verify m3's likes.
   667  		{
   668  			likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m3Pub))
   669  			require.NoError(err)
   670  			require.Equal(len(m3Likes), len(likedPostHashes))
   671  			for ii := 0; ii < len(likedPostHashes); ii++ {
   672  				require.Contains(m3Likes, *likedPostHashes[ii])
   673  			}
   674  		}
   675  	}
   676  	testConnectedState()
   677  
   678  	// Disconnect the transactions from a single view in the same way as above
   679  	// i.e. without flushing each time.
   680  	utxoView2, err := NewUtxoView(db, params, nil)
   681  	require.NoError(err)
   682  	for ii := 0; ii < len(txnOps); ii++ {
   683  		backwardIter := len(txnOps) - 1 - ii
   684  		fmt.Printf("Disconnecting transaction with index %d (going backwards)\n", backwardIter)
   685  		currentOps := txnOps[backwardIter]
   686  		currentTxn := txns[backwardIter]
   687  
   688  		currentHash := currentTxn.Hash()
   689  		err = utxoView2.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight)
   690  		require.NoError(err)
   691  	}
   692  	require.NoError(utxoView2.FlushToDb())
   693  	require.Equal(expectedSenderBalances[0], _getBalance(t, chain, nil, senderPkString))
   694  	require.Equal(expectedRecipientBalances[0], _getBalance(t, chain, nil, recipientPkString))
   695  
   696  	testDisconnectedState()
   697  
   698  	// All the txns should be in the mempool already so mining a block should put
   699  	// all those transactions in it.
   700  	block, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
   701  	require.NoError(err)
   702  	// Add one for the block reward. Now we have a meaty block.
   703  	require.Equal(len(txnOps)+1, len(block.Txns))
   704  	// Estimate the transaction fees of the tip block in various ways.
   705  	{
   706  		// Threshold above what's in the block should return the default fee at all times.
   707  		require.Equal(int64(0), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 0)))
   708  		require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 7)))
   709  		// Threshold below what's in the block should return the max of the median
   710  		// and the minfee. This means with a low minfee the value returned should be
   711  		// higher. And with a high minfee the value returned should be equal to the
   712  		// fee.
   713  		require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 7)))
   714  		require.Equal(int64(4), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 0)))
   715  		require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.01, 7)))
   716  		require.Equal(int64(4), int64(chain.EstimateDefaultFeeRateNanosPerKB(.01, 1)))
   717  	}
   718  
   719  	testConnectedState()
   720  
   721  	// Roll back the block and make sure we don't hit any errors.
   722  	{
   723  		utxoView, err := NewUtxoView(db, params, nil)
   724  		require.NoError(err)
   725  
   726  		// Fetch the utxo operations for the block we're detaching. We need these
   727  		// in order to be able to detach the block.
   728  		hash, err := block.Header.Hash()
   729  		require.NoError(err)
   730  		utxoOps, err := GetUtxoOperationsForBlock(db, hash)
   731  		require.NoError(err)
   732  
   733  		// Compute the hashes for all the transactions.
   734  		txHashes, err := ComputeTransactionHashes(block.Txns)
   735  		require.NoError(err)
   736  		require.NoError(utxoView.DisconnectBlock(block, txHashes, utxoOps))
   737  
   738  		// Flushing the view after applying and rolling back should work.
   739  		require.NoError(utxoView.FlushToDb())
   740  	}
   741  
   742  	testDisconnectedState()
   743  }