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

     1  package lib
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/btcsuite/btcd/btcec"
     6  	"github.com/dgraph-io/badger/v3"
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  func _privateMessage(t *testing.T, chain *Blockchain, db *badger.DB,
    14  	params *DeSoParams, feeRateNanosPerKB uint64, senderPkBase58Check string,
    15  	recipientPkBase58Check string,
    16  	senderPrivBase58Check string, unencryptedMessageText string, tstampNanos uint64) (
    17  	_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
    18  
    19  	assert := assert.New(t)
    20  	require := require.New(t)
    21  	_ = assert
    22  	_ = require
    23  
    24  	senderPkBytes, _, err := Base58CheckDecode(senderPkBase58Check)
    25  	require.NoError(err)
    26  
    27  	recipientPkBytes, _, err := Base58CheckDecode(recipientPkBase58Check)
    28  	require.NoError(err)
    29  
    30  	utxoView, err := NewUtxoView(db, params, nil)
    31  	require.NoError(err)
    32  
    33  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreatePrivateMessageTxn(
    34  		senderPkBytes, recipientPkBytes, unencryptedMessageText, "",
    35  		tstampNanos, feeRateNanosPerKB, nil, []*DeSoOutput{})
    36  	if err != nil {
    37  		return nil, nil, 0, err
    38  	}
    39  
    40  	require.Equal(totalInputMake, changeAmountMake+feesMake)
    41  
    42  	// Sign the transaction now that its inputs are set up.
    43  	_signTxn(t, txn, senderPrivBase58Check)
    44  
    45  	txHash := txn.Hash()
    46  	// Always use height+1 for validation since it's assumed the transaction will
    47  	// get mined into the next block.
    48  	blockHeight := chain.blockTip().Height + 1
    49  	utxoOps, totalInput, totalOutput, fees, err :=
    50  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
    51  	// ConnectTransaction should treat the amount locked as contributing to the
    52  	// output.
    53  	if err != nil {
    54  		return nil, nil, 0, err
    55  	}
    56  	require.Equal(totalInput, totalOutput+fees)
    57  	require.Equal(totalInput, totalInputMake)
    58  
    59  	// We should have one SPEND UtxoOperation for each input, one ADD operation
    60  	// for each output, and one OperationTypePrivateMessage operation at the end.
    61  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
    62  	for ii := 0; ii < len(txn.TxInputs); ii++ {
    63  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
    64  	}
    65  	require.Equal(OperationTypePrivateMessage, utxoOps[len(utxoOps)-1].Type)
    66  
    67  	require.NoError(utxoView.FlushToDb())
    68  
    69  	return utxoOps, txn, blockHeight, nil
    70  }
    71  
    72  func TestPrivateMessage(t *testing.T) {
    73  	assert := assert.New(t)
    74  	require := require.New(t)
    75  	_ = assert
    76  	_ = require
    77  
    78  	chain, params, db := NewLowDifficultyBlockchain()
    79  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
    80  
    81  	// Mine a few blocks to give the senderPkString some money.
    82  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
    83  	require.NoError(err)
    84  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
    85  	require.NoError(err)
    86  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
    87  	require.NoError(err)
    88  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
    89  	require.NoError(err)
    90  
    91  	// Setup some convenience functions for the test.
    92  	txnOps := [][]*UtxoOperation{}
    93  	txns := []*MsgDeSoTxn{}
    94  	expectedSenderBalances := []uint64{}
    95  	expectedRecipientBalances := []uint64{}
    96  
    97  	// We take the block tip to be the blockchain height rather than the
    98  	// header chain height.
    99  	savedHeight := chain.blockTip().Height + 1
   100  	registerOrTransfer := func(username string,
   101  		senderPk string, recipientPk string, senderPriv string) {
   102  
   103  		expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, senderPkString))
   104  		expectedRecipientBalances = append(expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString))
   105  
   106  		currentOps, currentTxn, _ := _doBasicTransferWithViewFlush(
   107  			t, chain, db, params, senderPk, recipientPk,
   108  			senderPriv, 7 /*amount to send*/, 11 /*feerate*/)
   109  
   110  		txnOps = append(txnOps, currentOps)
   111  		txns = append(txns, currentTxn)
   112  	}
   113  
   114  	// Fund all the keys.
   115  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   116  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   117  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   118  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   119  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   120  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   121  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   122  	registerOrTransfer("", senderPkString, m2Pub, senderPrivString)
   123  	registerOrTransfer("", senderPkString, m2Pub, senderPrivString)
   124  	registerOrTransfer("", senderPkString, m3Pub, senderPrivString)
   125  	registerOrTransfer("", senderPkString, m3Pub, senderPrivString)
   126  	registerOrTransfer("", senderPkString, m3Pub, senderPrivString)
   127  
   128  	privateMessage := func(
   129  		senderPkBase58Check string, recipientPkBase58Check string,
   130  		senderPrivBase58Check string, unencryptedMessageText string, tstampNanos uint64,
   131  		feeRateNanosPerKB uint64) {
   132  
   133  		expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, senderPkString))
   134  		expectedRecipientBalances = append(expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString))
   135  
   136  		currentOps, currentTxn, _, err := _privateMessage(
   137  			t, chain, db, params, feeRateNanosPerKB, senderPkBase58Check,
   138  			recipientPkBase58Check, senderPrivBase58Check, unencryptedMessageText, tstampNanos)
   139  		require.NoError(err)
   140  
   141  		txnOps = append(txnOps, currentOps)
   142  		txns = append(txns, currentTxn)
   143  	}
   144  
   145  	// ===================================================================================
   146  	// Do some PrivateMessage transactions
   147  	// ===================================================================================
   148  	tstamp1 := uint64(time.Now().UnixNano())
   149  	message1 := string(append([]byte("message1: "), RandomBytes(100)...))
   150  	tstamp2 := uint64(time.Now().UnixNano())
   151  	message2 := string(append([]byte("message2: "), RandomBytes(100)...))
   152  	tstamp3 := uint64(time.Now().UnixNano())
   153  	message3 := string(append([]byte("message3: "), RandomBytes(100)...))
   154  	tstamp4 := uint64(time.Now().UnixNano())
   155  	message4 := string(append([]byte("message4: "), RandomBytes(100)...))
   156  	message5 := string(append([]byte("message5: "), RandomBytes(100)...))
   157  
   158  	// Message where the sender is the recipient should fail.
   159  	_, _, _, err = _privateMessage(
   160  		t, chain, db, params, 10 /*feeRateNanosPerKB*/, m0Pub,
   161  		m0Pub, m0Priv, "test" /*unencryptedMessageText*/, tstamp1)
   162  	require.Error(err)
   163  	require.Contains(err.Error(), RuleErrorPrivateMessageSenderPublicKeyEqualsRecipientPublicKey)
   164  
   165  	// Message with length too long should fail.
   166  	badMessage := string(append([]byte("badMessage: "),
   167  		RandomBytes(int32(params.MaxPrivateMessageLengthBytes))...))
   168  	_, _, _, err = _privateMessage(
   169  		t, chain, db, params, 0 /*feeRateNanosPerKB*/, m0Pub,
   170  		m1Pub, m0Priv, badMessage /*unencryptedMessageText*/, tstamp1)
   171  	require.Error(err)
   172  	require.Contains(err.Error(), RuleErrorPrivateMessageEncryptedTextLengthExceedsMax)
   173  
   174  	// Zero tstamp should fail.
   175  	_, _, _, err = _privateMessage(
   176  		t, chain, db, params, 0 /*feeRateNanosPerKB*/, m0Pub,
   177  		m1Pub, m0Priv, message1 /*unencryptedMessageText*/, 0)
   178  	require.Error(err)
   179  	require.Contains(err.Error(), RuleErrorPrivateMessageTstampIsZero)
   180  
   181  	// m0 -> m1: message1, tstamp1
   182  	privateMessage(
   183  		m0Pub, m1Pub, m0Priv, message1, tstamp1, 0 /*feeRateNanosPerKB*/)
   184  
   185  	// Duplicating (m0, tstamp1) should fail.
   186  	_, _, _, err = _privateMessage(
   187  		t, chain, db, params, 0 /*feeRateNanosPerKB*/, m0Pub,
   188  		m1Pub, m0Priv, message1 /*unencryptedMessageText*/, tstamp1)
   189  	require.Error(err)
   190  	require.Contains(err.Error(), RuleErrorPrivateMessageExistsWithSenderPublicKeyTstampTuple)
   191  
   192  	// Duplicating (m1, tstamp1) should fail.
   193  	_, _, _, err = _privateMessage(
   194  		t, chain, db, params, 0 /*feeRateNanosPerKB*/, m1Pub,
   195  		m0Pub, m1Priv, message1 /*unencryptedMessageText*/, tstamp1)
   196  	require.Error(err)
   197  	require.Contains(err.Error(), RuleErrorPrivateMessageExistsWithSenderPublicKeyTstampTuple)
   198  
   199  	// Duplicating (m0, tstamp1) with a different sender should still fail.
   200  	_, _, _, err = _privateMessage(
   201  		t, chain, db, params, 0 /*feeRateNanosPerKB*/, m2Pub,
   202  		m0Pub, m2Priv, message1 /*unencryptedMessageText*/, tstamp1)
   203  	require.Error(err)
   204  	require.Contains(err.Error(), RuleErrorPrivateMessageExistsWithRecipientPublicKeyTstampTuple)
   205  
   206  	// Duplicating (m1, tstamp1) with a different sender should still fail.
   207  	_, _, _, err = _privateMessage(
   208  		t, chain, db, params, 0 /*feeRateNanosPerKB*/, m2Pub,
   209  		m1Pub, m2Priv, message1 /*unencryptedMessageText*/, tstamp1)
   210  	require.Error(err)
   211  	require.Contains(err.Error(), RuleErrorPrivateMessageExistsWithRecipientPublicKeyTstampTuple)
   212  
   213  	// m2 -> m1: message2, tstamp2
   214  	privateMessage(
   215  		m2Pub, m1Pub, m2Priv, message2, tstamp2, 10 /*feeRateNanosPerKB*/)
   216  
   217  	// m3 -> m1: message3, tstamp3
   218  	privateMessage(
   219  		m3Pub, m1Pub, m3Priv, message3, tstamp3, 10 /*feeRateNanosPerKB*/)
   220  
   221  	// m2 -> m1: message4Str, tstamp4
   222  	privateMessage(
   223  		m1Pub, m2Pub, m1Priv, message4, tstamp4, 10 /*feeRateNanosPerKB*/)
   224  
   225  	// m2 -> m3: message5Str, tstamp1
   226  	// Using tstamp1 should be OK since the message is between two new users.
   227  	privateMessage(
   228  		m2Pub, m3Pub, m2Priv, message5, tstamp1, 10 /*feeRateNanosPerKB*/)
   229  
   230  	// Verify that the messages are as we expect them in the db.
   231  	// 1: m0 m1
   232  	// 2: m2 m1
   233  	// 3: m3 m1
   234  	// 4: m1 m2
   235  	// 5: m2 m3
   236  	// => m0: 1
   237  	// 		m1: 4
   238  	//    m2: 3
   239  	//    m3: 2
   240  	{
   241  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m0Pub))
   242  		require.NoError(err)
   243  		require.Equal(1, len(messages))
   244  		messageEntry := messages[0]
   245  		require.Equal(messageEntry.SenderPublicKey, _strToPk(t, m0Pub))
   246  		require.Equal(messageEntry.RecipientPublicKey, _strToPk(t, m1Pub))
   247  		require.Equal(messageEntry.TstampNanos, tstamp1)
   248  		require.Equal(messageEntry.isDeleted, false)
   249  		priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), _strToPk(t, m1Priv))
   250  		decryptedBytes, err := DecryptBytesWithPrivateKey(messageEntry.EncryptedText, priv.ToECDSA())
   251  		require.NoError(err)
   252  		require.Equal(message1, string(decryptedBytes))
   253  	}
   254  	{
   255  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m1Pub))
   256  		require.NoError(err)
   257  		require.Equal(4, len(messages))
   258  	}
   259  	{
   260  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m2Pub))
   261  		require.NoError(err)
   262  		require.Equal(3, len(messages))
   263  	}
   264  	{
   265  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m3Pub))
   266  		require.NoError(err)
   267  		require.Equal(2, len(messages))
   268  	}
   269  
   270  	// ===================================================================================
   271  	// Finish it off with some transactions
   272  	// ===================================================================================
   273  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   274  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   275  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   276  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   277  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   278  	registerOrTransfer("", senderPkString, m0Pub, senderPrivString)
   279  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   280  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   281  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   282  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   283  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   284  	registerOrTransfer("", senderPkString, m1Pub, senderPrivString)
   285  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   286  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   287  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   288  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   289  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   290  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   291  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   292  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   293  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   294  	registerOrTransfer("", m1Pub, m0Pub, m1Priv)
   295  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   296  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   297  	registerOrTransfer("", m0Pub, m1Pub, m0Priv)
   298  
   299  	// Roll back all of the above using the utxoOps from each.
   300  	for ii := 0; ii < len(txnOps); ii++ {
   301  		backwardIter := len(txnOps) - 1 - ii
   302  		currentOps := txnOps[backwardIter]
   303  		currentTxn := txns[backwardIter]
   304  		fmt.Printf("Disconnecting transaction with type %v index %d (going backwards)\n", currentTxn.TxnMeta.GetTxnType(), backwardIter)
   305  
   306  		utxoView, err := NewUtxoView(db, params, nil)
   307  		require.NoError(err)
   308  
   309  		currentHash := currentTxn.Hash()
   310  		err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight)
   311  		require.NoError(err)
   312  
   313  		require.NoError(utxoView.FlushToDb())
   314  
   315  		// After disconnecting, the balances should be restored to what they
   316  		// were before this transaction was applied.
   317  		require.Equal(expectedSenderBalances[backwardIter], _getBalance(t, chain, nil, senderPkString))
   318  		require.Equal(expectedRecipientBalances[backwardIter], _getBalance(t, chain, nil, recipientPkString))
   319  	}
   320  
   321  	// Verify that all the messages have been deleted.
   322  	{
   323  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m0Pub))
   324  		require.NoError(err)
   325  		require.Equal(0, len(messages))
   326  	}
   327  	{
   328  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m1Pub))
   329  		require.NoError(err)
   330  		require.Equal(0, len(messages))
   331  	}
   332  	{
   333  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m2Pub))
   334  		require.NoError(err)
   335  		require.Equal(0, len(messages))
   336  	}
   337  	{
   338  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m3Pub))
   339  		require.NoError(err)
   340  		require.Equal(0, len(messages))
   341  	}
   342  
   343  	// Apply all the transactions to a mempool object and make sure we don't get any
   344  	// errors. Verify the balances align as we go.
   345  	for ii, tx := range txns {
   346  		// See comment above on this transaction.
   347  		fmt.Printf("Adding txn %d of type %v to mempool\n", ii, tx.TxnMeta.GetTxnType())
   348  
   349  		require.Equal(expectedSenderBalances[ii], _getBalance(t, chain, mempool, senderPkString))
   350  		require.Equal(expectedRecipientBalances[ii], _getBalance(t, chain, mempool, recipientPkString))
   351  
   352  		acceptedTxns, err := mempool.ProcessTransaction(tx, false, false, 0, true)
   353  		require.NoError(err, "Problem adding transaction %d to mempool: %v", ii, tx)
   354  		require.Equal(1, len(acceptedTxns))
   355  	}
   356  
   357  	// Apply all the transactions to a view and flush the view to the db.
   358  	utxoView, err := NewUtxoView(db, params, nil)
   359  	require.NoError(err)
   360  	for ii, txn := range txns {
   361  		fmt.Printf("Adding txn %v of type %v to UtxoView\n", ii, txn.TxnMeta.GetTxnType())
   362  
   363  		// Always use height+1 for validation since it's assumed the transaction will
   364  		// get mined into the next block.
   365  		txHash := txn.Hash()
   366  		blockHeight := chain.blockTip().Height + 1
   367  		_, _, _, _, err :=
   368  			utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   369  		require.NoError(err)
   370  	}
   371  	// Flush the utxoView after having added all the transactions.
   372  	require.NoError(utxoView.FlushToDb())
   373  
   374  	// Disonnect the transactions from a single view in the same way as above
   375  	// i.e. without flushing each time.
   376  	utxoView2, err := NewUtxoView(db, params, nil)
   377  	require.NoError(err)
   378  	for ii := 0; ii < len(txnOps); ii++ {
   379  		backwardIter := len(txnOps) - 1 - ii
   380  		fmt.Printf("Disconnecting transaction with index %d (going backwards)\n", backwardIter)
   381  		currentOps := txnOps[backwardIter]
   382  		currentTxn := txns[backwardIter]
   383  
   384  		currentHash := currentTxn.Hash()
   385  		err = utxoView2.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight)
   386  		require.NoError(err)
   387  	}
   388  	require.NoError(utxoView2.FlushToDb())
   389  	require.Equal(expectedSenderBalances[0], _getBalance(t, chain, nil, senderPkString))
   390  	require.Equal(expectedRecipientBalances[0], _getBalance(t, chain, nil, recipientPkString))
   391  
   392  	// Verify that all the messages have been deleted.
   393  	{
   394  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m0Pub))
   395  		require.NoError(err)
   396  		require.Equal(0, len(messages))
   397  	}
   398  	{
   399  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m1Pub))
   400  		require.NoError(err)
   401  		require.Equal(0, len(messages))
   402  	}
   403  	{
   404  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m2Pub))
   405  		require.NoError(err)
   406  		require.Equal(0, len(messages))
   407  	}
   408  	{
   409  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m3Pub))
   410  		require.NoError(err)
   411  		require.Equal(0, len(messages))
   412  	}
   413  
   414  	// Try and estimate the fees in a situation where the last block contains just a
   415  	// block reward.
   416  	{
   417  		// Fee should just equal the min passed in because the block has so few transactions.
   418  		require.Equal(int64(0), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 0)))
   419  		require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 7)))
   420  		require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 7)))
   421  		require.Equal(int64(0), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 0)))
   422  		require.Equal(int64(1), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 1)))
   423  		require.Equal(int64(1), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 1)))
   424  	}
   425  
   426  	// All the txns should be in the mempool already so mining a block should put
   427  	// all those transactions in it.
   428  	block, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
   429  	require.NoError(err)
   430  	// Add one for the block reward. Now we have a meaty block.
   431  	require.Equal(len(txnOps)+1, len(block.Txns))
   432  	// Estimate the transaction fees of the tip block in various ways.
   433  	{
   434  		// Threshold above what's in the block should return the default fee at all times.
   435  		require.Equal(int64(0), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 0)))
   436  		require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 7)))
   437  		// Threshold below what's in the block should return the max of the median
   438  		// and the minfee. This means with a low minfee the value returned should be
   439  		// higher. And with a high minfee the value returned should be equal to the
   440  		// fee.
   441  		require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 7)))
   442  		require.Equal(int64(4), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 0)))
   443  		require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.01, 7)))
   444  		require.Equal(int64(4), int64(chain.EstimateDefaultFeeRateNanosPerKB(.01, 1)))
   445  	}
   446  
   447  	// Roll back the block and make sure we don't hit any errors.
   448  	{
   449  		utxoView, err := NewUtxoView(db, params, nil)
   450  		require.NoError(err)
   451  
   452  		// Fetch the utxo operations for the block we're detaching. We need these
   453  		// in order to be able to detach the block.
   454  		hash, err := block.Header.Hash()
   455  		require.NoError(err)
   456  		utxoOps, err := GetUtxoOperationsForBlock(db, hash)
   457  		require.NoError(err)
   458  
   459  		// Compute the hashes for all the transactions.
   460  		txHashes, err := ComputeTransactionHashes(block.Txns)
   461  		require.NoError(err)
   462  		require.NoError(utxoView.DisconnectBlock(block, txHashes, utxoOps))
   463  
   464  		// Flushing the view after applying and rolling back should work.
   465  		require.NoError(utxoView.FlushToDb())
   466  	}
   467  
   468  	// Verify that all the messages have been deleted.
   469  	{
   470  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m0Pub))
   471  		require.NoError(err)
   472  		require.Equal(0, len(messages))
   473  	}
   474  	{
   475  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m1Pub))
   476  		require.NoError(err)
   477  		require.Equal(0, len(messages))
   478  	}
   479  	{
   480  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m2Pub))
   481  		require.NoError(err)
   482  		require.Equal(0, len(messages))
   483  	}
   484  	{
   485  		messages, err := DbGetMessageEntriesForPublicKey(db, _strToPk(t, m3Pub))
   486  		require.NoError(err)
   487  		require.Equal(0, len(messages))
   488  	}
   489  }