github.com/deso-protocol/core@v1.2.9/lib/block_view_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  	_ "net/http/pprof"
    11  )
    12  
    13  func _strToPk(t *testing.T, pkStr string) []byte {
    14  	require := require.New(t)
    15  
    16  	pkBytes, _, err := Base58CheckDecode(pkStr)
    17  	require.NoError(err)
    18  
    19  	return pkBytes
    20  }
    21  
    22  func getTxnSize(txn MsgDeSoTxn) int64 {
    23  	bytes, _ := txn.ToBytes(false)
    24  	return int64(len(bytes))
    25  }
    26  
    27  var (
    28  	// Set up some addresses
    29  	m0Pub           = "tBCKY2X1Gbqn95tN1PfsCFLKX6x6h48g5LdHt9T95Wj9Rm6EVKLVpi"
    30  	m0Priv          = "tbc2uXFwv3CJvr5HdLLKpAtLNCtBafvfxLBMbJFCNdLA61cLB7aLq"
    31  	m0PkBytes, _, _ = Base58CheckDecode(m0Pub)
    32  
    33  	m1Pub           = "tBCKYGWj36qERG57RKdrnCf6JQad1smGTzeLkj1bfN7UqKwY8SM57a"
    34  	m1Priv          = "tbc2DtxgxPVB6T6sbFqhgNrPqwb7QUYG5ZS7aEXQ3ZxAyG88YAPVy"
    35  	m1PkBytes, _, _ = Base58CheckDecode(m1Pub)
    36  
    37  	m2Pub           = "tBCKVNYw7WgG59SGP8EdpR9nyywoMBYa3ChLG4UjCBhvFgd4e7oXNg"
    38  	m2Priv          = "tbc37VGdu4RJ7uJcoGHrDJkr4FZPsVYbyo3dRxdhyQHPNp6jUjbK1"
    39  	m2PkBytes, _, _ = Base58CheckDecode(m2Pub)
    40  
    41  	m3Pub           = "tBCKWqMGE7xdz78juDSEsDFYt67CuL9VrTiv627Wj2sLwG6B2fcy7o"
    42  	m3Priv          = "tbc2MkEWaCoVNh5rV4fyAdSmAkLQ9bZLqEMGSLYtoAAxgA1844Y67"
    43  	m3PkBytes, _, _ = Base58CheckDecode(m3Pub)
    44  
    45  	m4Pub           = "tBCKWu6nNQa3cUV8QLwRhX9r6NXcNpDuK7xtscwm27zXJ7MxdnmZ3g"
    46  	m4Priv          = "tbc2GmpAmkm8CmMjS9NXiAFZHEDGqxSCCpkvkwnY8oqfZXAXnmtFV"
    47  	m4PkBytes, _, _ = Base58CheckDecode(m4Pub)
    48  
    49  	m5Pub           = "tBCKWWAqRR89yCLGEbw2QXK32XZkgEacnrZbdc1KrXk5NzeDvfTr4h"
    50  	m5Priv          = "tbc2w7CpjUTcmtLdAPxb8BwYQ8W66Qn8hDcgLxyHGJWfbuT4RFtjz"
    51  	m5PkBytes, _, _ = Base58CheckDecode(m5Pub)
    52  
    53  	m6Pub           = "tBCKX5xzB91EPszJq6Ep4AHf7nKi9BXBFeb7o668N3bryz5deqvCBo"
    54  	m6Priv          = "tbc2hN9pnZVnA8TCtV76tZKt5wfLsHyQ5jo9s7NxRswa1h5Y4Hbgg"
    55  	m6PkBytes, _, _ = Base58CheckDecode(m6Pub)
    56  
    57  	paramUpdaterPub           = "tBCKWVdVW6St5R8KkbQYd9uhvwmna4EVAeEKBXRsZLVrCM1JHkEU1G"
    58  	paramUpdaterPriv          = "tbc1jF5hXKspbYUVqkSwyyrs9oSho8yA6vZURvBNLySVESFsRmaGf"
    59  	paramUpdaterPkBytes, _, _ = Base58CheckDecode(paramUpdaterPub)
    60  )
    61  
    62  func _doBasicTransferWithViewFlush(t *testing.T, chain *Blockchain, db *badger.DB,
    63  	params *DeSoParams, pkSenderStr string, pkReceiverStr string, privStr string,
    64  	amountNanos uint64, feeRateNanosPerKB uint64) (
    65  	_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32) {
    66  
    67  	assert := assert.New(t)
    68  	require := require.New(t)
    69  	_, _ = assert, require
    70  
    71  	txn := _assembleBasicTransferTxnFullySigned(
    72  		t, chain, amountNanos, feeRateNanosPerKB, pkSenderStr, pkReceiverStr, privStr, nil)
    73  
    74  	utxoView, err := NewUtxoView(db, params, nil)
    75  	require.NoError(err)
    76  
    77  	// Always use height+1 for validation since it's assumed the transaction will
    78  	// get mined into the next block.
    79  	txHash := txn.Hash()
    80  	blockHeight := chain.blockTip().Height + 1
    81  	utxoOps, totalInput, totalOutput, fees, err :=
    82  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
    83  	require.NoError(err)
    84  	require.GreaterOrEqual(totalOutput, amountNanos)
    85  	require.Equal(totalInput, totalOutput+fees)
    86  
    87  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs), len(utxoOps))
    88  	for ii := 0; ii < len(txn.TxInputs); ii++ {
    89  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
    90  	}
    91  	for ii := len(txn.TxInputs); ii < len(txn.TxInputs)+len(txn.TxOutputs); ii++ {
    92  		require.Equal(OperationTypeAddUtxo, utxoOps[ii].Type)
    93  	}
    94  
    95  	require.NoError(utxoView.FlushToDb())
    96  
    97  	return utxoOps, txn, blockHeight
    98  }
    99  
   100  func _registerOrTransferWithTestMeta(testMeta *TestMeta, username string,
   101  	senderPk string, recipientPk string, senderPriv string, amountToSend uint64) {
   102  
   103  	testMeta.expectedSenderBalances = append(
   104  		testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, senderPk))
   105  
   106  	currentOps, currentTxn, _ := _doBasicTransferWithViewFlush(
   107  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params, senderPk, recipientPk,
   108  		senderPriv, amountToSend, 11 /*feerate*/)
   109  
   110  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   111  	testMeta.txns = append(testMeta.txns, currentTxn)
   112  }
   113  
   114  func _updateGlobalParamsEntry(t *testing.T, chain *Blockchain, db *badger.DB,
   115  	params *DeSoParams, feeRateNanosPerKB uint64, updaterPkBase58Check string,
   116  	updaterPrivBase58Check string, usdCentsPerBitcoin int64, minimumNetworkFeesNanosPerKB int64,
   117  	createProfileFeeNanos int64, createNFTFeeNanos int64, maxCopiesPerNFT int64, flushToDb bool) (
   118  	_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
   119  
   120  	assert := assert.New(t)
   121  	require := require.New(t)
   122  	_ = assert
   123  	_ = require
   124  
   125  	updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check)
   126  	require.NoError(err)
   127  
   128  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateUpdateGlobalParamsTxn(
   129  		updaterPkBytes,
   130  		usdCentsPerBitcoin,
   131  		createProfileFeeNanos,
   132  		createNFTFeeNanos,
   133  		maxCopiesPerNFT,
   134  		minimumNetworkFeesNanosPerKB,
   135  		nil,
   136  		feeRateNanosPerKB,
   137  		nil,
   138  		[]*DeSoOutput{})
   139  	if err != nil {
   140  		return nil, nil, 0, err
   141  	}
   142  
   143  	require.Equal(totalInputMake, changeAmountMake+feesMake)
   144  
   145  	// Sign the transaction now that its inputs are set up.
   146  	_signTxn(t, txn, updaterPrivBase58Check)
   147  
   148  	utxoView, err := NewUtxoView(db, params, nil)
   149  	require.NoError(err)
   150  
   151  	txHash := txn.Hash()
   152  	// Always use height+1 for validation since it's assumed the transaction will
   153  	// get mined into the next block.
   154  	blockHeight := chain.blockTip().Height + 1
   155  	utxoOps, totalInput, totalOutput, fees, err :=
   156  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   157  	// ConnectTransaction should treat the amount locked as contributing to the
   158  	// output.
   159  	if err != nil {
   160  		return nil, nil, 0, err
   161  	}
   162  	require.Equal(totalInput, totalOutput+fees)
   163  	require.Equal(totalInput, totalInputMake)
   164  
   165  	// We should have one SPEND UtxoOperation for each input, one ADD operation
   166  	// for each output, and one OperationTypePrivateMessage operation at the end.
   167  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
   168  	for ii := 0; ii < len(txn.TxInputs); ii++ {
   169  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
   170  	}
   171  	require.Equal(
   172  		OperationTypeUpdateGlobalParams, utxoOps[len(utxoOps)-1].Type)
   173  	if flushToDb {
   174  		require.NoError(utxoView.FlushToDb())
   175  	}
   176  	return utxoOps, txn, blockHeight, nil
   177  }
   178  
   179  func _updateGlobalParamsEntryWithTestMeta(
   180  	testMeta *TestMeta,
   181  	feeRateNanosPerKB uint64,
   182  	updaterPkBase58Check string,
   183  	updaterPrivBase58Check string,
   184  	USDCentsPerBitcoinExchangeRate int64,
   185  	minimumNetworkFeeNanosPerKb int64,
   186  	createProfileFeeNanos int64,
   187  	createNFTFeeNanos int64,
   188  	maxCopiesPerNFT int64,
   189  ) {
   190  
   191  	testMeta.expectedSenderBalances = append(
   192  		testMeta.expectedSenderBalances,
   193  		_getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check))
   194  
   195  	currentOps, currentTxn, _, err := _updateGlobalParamsEntry(
   196  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params,
   197  		feeRateNanosPerKB,
   198  		updaterPkBase58Check,
   199  		updaterPrivBase58Check,
   200  		int64(InitialUSDCentsPerBitcoinExchangeRate),
   201  		minimumNetworkFeeNanosPerKb,
   202  		createProfileFeeNanos,
   203  		createNFTFeeNanos,
   204  		maxCopiesPerNFT,
   205  		true) /*flushToDB*/
   206  	require.NoError(testMeta.t, err)
   207  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   208  	testMeta.txns = append(testMeta.txns, currentTxn)
   209  }
   210  
   211  type TestMeta struct {
   212  	t                      *testing.T
   213  	chain                  *Blockchain
   214  	db                     *badger.DB
   215  	params                 *DeSoParams
   216  	mempool                *DeSoMempool
   217  	miner                  *DeSoMiner
   218  	txnOps                 [][]*UtxoOperation
   219  	txns                   []*MsgDeSoTxn
   220  	expectedSenderBalances []uint64
   221  	savedHeight            uint32
   222  }
   223  
   224  func _rollBackTestMetaTxnsAndFlush(testMeta *TestMeta) {
   225  	// Roll back all of the above using the utxoOps from each.
   226  	for ii := 0; ii < len(testMeta.txnOps); ii++ {
   227  		backwardIter := len(testMeta.txnOps) - 1 - ii
   228  		currentOps := testMeta.txnOps[backwardIter]
   229  		currentTxn := testMeta.txns[backwardIter]
   230  		fmt.Printf(
   231  			"Disconnecting transaction with type %v index %d (going backwards)\n",
   232  			currentTxn.TxnMeta.GetTxnType(), backwardIter)
   233  
   234  		utxoView, err := NewUtxoView(testMeta.db, testMeta.params, nil)
   235  		require.NoError(testMeta.t, err)
   236  
   237  		currentHash := currentTxn.Hash()
   238  		err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, testMeta.savedHeight)
   239  		require.NoError(testMeta.t, err)
   240  
   241  		require.NoError(testMeta.t, utxoView.FlushToDb())
   242  
   243  		// After disconnecting, the balances should be restored to what they
   244  		// were before this transaction was applied.
   245  		require.Equal(
   246  			testMeta.t,
   247  			testMeta.expectedSenderBalances[backwardIter],
   248  			_getBalance(testMeta.t, testMeta.chain, nil, PkToStringTestnet(currentTxn.PublicKey)),
   249  		)
   250  	}
   251  }
   252  
   253  func _applyTestMetaTxnsToMempool(testMeta *TestMeta) {
   254  	// Apply all the transactions to a mempool object and make sure we don't get any
   255  	// errors. Verify the balances align as we go.
   256  	for ii, tx := range testMeta.txns {
   257  		require.Equal(
   258  			testMeta.t,
   259  			testMeta.expectedSenderBalances[ii],
   260  			_getBalance(testMeta.t, testMeta.chain, testMeta.mempool, PkToStringTestnet(tx.PublicKey)))
   261  
   262  		fmt.Printf("Adding txn %d of type %v to mempool\n", ii, tx.TxnMeta.GetTxnType())
   263  
   264  		_, err := testMeta.mempool.ProcessTransaction(tx, false, false, 0, true)
   265  		require.NoError(testMeta.t, err, "Problem adding transaction %d to mempool: %v", ii, tx)
   266  	}
   267  }
   268  
   269  func _applyTestMetaTxnsToViewAndFlush(testMeta *TestMeta) {
   270  	// Apply all the transactions to a view and flush the view to the db.
   271  	utxoView, err := NewUtxoView(testMeta.db, testMeta.params, nil)
   272  	require.NoError(testMeta.t, err)
   273  	for ii, txn := range testMeta.txns {
   274  		fmt.Printf("Adding txn %v of type %v to UtxoView\n", ii, txn.TxnMeta.GetTxnType())
   275  
   276  		// Always use height+1 for validation since it's assumed the transaction will
   277  		// get mined into the next block.
   278  		txHash := txn.Hash()
   279  		blockHeight := testMeta.chain.blockTip().Height + 1
   280  		_, _, _, _, err =
   281  			utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   282  		require.NoError(testMeta.t, err)
   283  	}
   284  	// Flush the utxoView after having added all the transactions.
   285  	require.NoError(testMeta.t, utxoView.FlushToDb())
   286  }
   287  
   288  func _disconnectTestMetaTxnsFromViewAndFlush(testMeta *TestMeta) {
   289  	// Disonnect the transactions from a single view in the same way as above
   290  	// i.e. without flushing each time.
   291  	utxoView, err := NewUtxoView(testMeta.db, testMeta.params, nil)
   292  	require.NoError(testMeta.t, err)
   293  	for ii := 0; ii < len(testMeta.txnOps); ii++ {
   294  		backwardIter := len(testMeta.txnOps) - 1 - ii
   295  		fmt.Printf("Disconnecting transaction with index %d (going backwards)\n", backwardIter)
   296  		currentOps := testMeta.txnOps[backwardIter]
   297  		currentTxn := testMeta.txns[backwardIter]
   298  
   299  		currentHash := currentTxn.Hash()
   300  		err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, testMeta.savedHeight)
   301  		require.NoError(testMeta.t, err)
   302  	}
   303  	require.NoError(testMeta.t, utxoView.FlushToDb())
   304  	require.Equal(
   305  		testMeta.t,
   306  		testMeta.expectedSenderBalances[0],
   307  		_getBalance(testMeta.t, testMeta.chain, nil, senderPkString))
   308  }
   309  
   310  func _connectBlockThenDisconnectBlockAndFlush(testMeta *TestMeta) {
   311  	// all those transactions in it.
   312  	block, err := testMeta.miner.MineAndProcessSingleBlock(0 /*threadIndex*/, testMeta.mempool)
   313  	require.NoError(testMeta.t, err)
   314  	// Add one for the block reward. Now we have a meaty block.
   315  	require.Equal(testMeta.t, len(testMeta.txnOps)+1, len(block.Txns))
   316  
   317  	// Roll back the block and make sure we don't hit any errors.
   318  	{
   319  		utxoView, err := NewUtxoView(testMeta.db, testMeta.params, nil)
   320  		require.NoError(testMeta.t, err)
   321  
   322  		// Fetch the utxo operations for the block we're detaching. We need these
   323  		// in order to be able to detach the block.
   324  		hash, err := block.Header.Hash()
   325  		require.NoError(testMeta.t, err)
   326  		utxoOps, err := GetUtxoOperationsForBlock(testMeta.db, hash)
   327  		require.NoError(testMeta.t, err)
   328  
   329  		// Compute the hashes for all the transactions.
   330  		txHashes, err := ComputeTransactionHashes(block.Txns)
   331  		require.NoError(testMeta.t, err)
   332  		require.NoError(testMeta.t, utxoView.DisconnectBlock(block, txHashes, utxoOps))
   333  
   334  		// Flushing the view after applying and rolling back should work.
   335  		require.NoError(testMeta.t, utxoView.FlushToDb())
   336  	}
   337  }
   338  
   339  func TestUpdateGlobalParams(t *testing.T) {
   340  	// Set up a blockchain
   341  	assert := assert.New(t)
   342  	require := require.New(t)
   343  	_, _ = assert, require
   344  
   345  	chain, params, db := NewLowDifficultyBlockchain()
   346  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
   347  	_, _ = mempool, miner
   348  
   349  	// Set the founder equal to the moneyPk
   350  	params.ParamUpdaterPublicKeys = make(map[PkMapKey]bool)
   351  	params.ParamUpdaterPublicKeys[MakePkMapKey(MustBase58CheckDecode(moneyPkString))] = true
   352  
   353  	// Send money to m0 from moneyPk
   354  	_, _, _ = _doBasicTransferWithViewFlush(
   355  		t, chain, db, params, moneyPkString, m0Pub,
   356  		moneyPrivString, 10*NanosPerUnit /*amount to send*/, 11 /*feerate*/)
   357  
   358  	// Should fail when founder key is not equal to moneyPk
   359  	{
   360  		newUSDCentsPerBitcoin := int64(27000 * 100)
   361  		newMinimumNetworkFeeNanosPerKB := int64(100)
   362  		newCreateProfileFeeNanos := int64(200)
   363  		newCreateNFTFeeNanos := int64(300)
   364  		_, _, _, err := _updateGlobalParamsEntry(
   365  			t, chain, db, params, 100, /*feeRateNanosPerKB*/
   366  			m0Pub,
   367  			m0Priv,
   368  			newUSDCentsPerBitcoin,
   369  			newMinimumNetworkFeeNanosPerKB,
   370  			newCreateProfileFeeNanos,
   371  			newCreateNFTFeeNanos,
   372  			-1, /*maxCopiesPerNFT*/
   373  			false)
   374  		require.Error(err)
   375  		require.Contains(err.Error(), RuleErrorUserNotAuthorizedToUpdateGlobalParams)
   376  	}
   377  
   378  	// Should pass when founder key is equal to moneyPk
   379  	var updateGlobalParamsTxn *MsgDeSoTxn
   380  	var err error
   381  	{
   382  		newUSDCentsPerBitcoin := int64(270430 * 100)
   383  		newMinimumNetworkFeeNanosPerKB := int64(191)
   384  		newCreateProfileFeeNanos := int64(10015)
   385  		newCreateNFTFeeNanos := int64(14983)
   386  		newMaxCopiesPerNFT := int64(123)
   387  		_, updateGlobalParamsTxn, _, err = _updateGlobalParamsEntry(
   388  			t, chain, db, params, 200, /*feeRateNanosPerKB*/
   389  			moneyPkString,
   390  			moneyPrivString,
   391  			newUSDCentsPerBitcoin,
   392  			newMinimumNetworkFeeNanosPerKB,
   393  			newCreateProfileFeeNanos,
   394  			newCreateNFTFeeNanos,
   395  			newMaxCopiesPerNFT,
   396  			false)
   397  		require.NoError(err)
   398  
   399  		utxoView, err := NewUtxoView(db, params, nil)
   400  		require.NoError(err)
   401  		txnSize := getTxnSize(*updateGlobalParamsTxn)
   402  		blockHeight := chain.blockTip().Height + 1
   403  		utxoOps, totalInput, totalOutput, fees, err :=
   404  			utxoView.ConnectTransaction(updateGlobalParamsTxn,
   405  				updateGlobalParamsTxn.Hash(), txnSize, blockHeight, true, /*verifySignature*/
   406  				false /*ignoreUtxos*/)
   407  		require.NoError(err)
   408  		_, _, _, _ = utxoOps, totalInput, totalOutput, fees
   409  		require.NoError(utxoView.FlushToDb())
   410  
   411  		// Verify that utxoView and db reflect the new global parmas entry.
   412  		expectedGlobalParams := GlobalParamsEntry{
   413  			USDCentsPerBitcoin:          uint64(newUSDCentsPerBitcoin),
   414  			MinimumNetworkFeeNanosPerKB: uint64(newMinimumNetworkFeeNanosPerKB),
   415  			CreateProfileFeeNanos:       uint64(newCreateProfileFeeNanos),
   416  			CreateNFTFeeNanos:           uint64(newCreateNFTFeeNanos),
   417  			MaxCopiesPerNFT:             123,
   418  		}
   419  		require.Equal(DbGetGlobalParamsEntry(utxoView.Handle), &expectedGlobalParams)
   420  
   421  		require.Equal(utxoView.GlobalParamsEntry, &expectedGlobalParams)
   422  
   423  		// Check the balance of the updater after this txn
   424  		require.NotEqual(0, _getBalance(t, chain, nil, moneyPkString))
   425  	}
   426  
   427  	{
   428  
   429  		// Save the prev global params entry so we can check it after disconnect.
   430  		prevGlobalParams := DbGetGlobalParamsEntry(db)
   431  
   432  		newUSDCentsPerBitcoin := int64(270434 * 100)
   433  		newMinimumNetworkFeeNanosPerKB := int64(131)
   434  		newCreateProfileFeeNanos := int64(102315)
   435  		newCreateNFTFeeNanos := int64(3244099)
   436  		newMaxCopiesPerNFT := int64(555)
   437  		var utxoOps []*UtxoOperation
   438  		utxoOps, updateGlobalParamsTxn, _, err = _updateGlobalParamsEntry(
   439  			t, chain, db, params, 200, /*feeRateNanosPerKB*/
   440  			moneyPkString,
   441  			moneyPrivString,
   442  			newUSDCentsPerBitcoin,
   443  			newMinimumNetworkFeeNanosPerKB,
   444  			newCreateProfileFeeNanos,
   445  			newCreateNFTFeeNanos,
   446  			newMaxCopiesPerNFT, /*maxCopiesPerNFT*/
   447  			true)
   448  		require.NoError(err)
   449  
   450  		// Verify that the db reflects the new global params entry.
   451  		expectedGlobalParams := &GlobalParamsEntry{
   452  			USDCentsPerBitcoin:          uint64(newUSDCentsPerBitcoin),
   453  			MinimumNetworkFeeNanosPerKB: uint64(newMinimumNetworkFeeNanosPerKB),
   454  			CreateProfileFeeNanos:       uint64(newCreateProfileFeeNanos),
   455  			CreateNFTFeeNanos:           uint64(newCreateNFTFeeNanos),
   456  			MaxCopiesPerNFT:             uint64(newMaxCopiesPerNFT),
   457  		}
   458  
   459  		require.Equal(DbGetGlobalParamsEntry(db), expectedGlobalParams)
   460  
   461  		// Now let's do a disconnect and make sure the values reflect the previous entry.
   462  		utxoView, err := NewUtxoView(db, params, nil)
   463  		require.NoError(err)
   464  		blockHeight := chain.blockTip().Height + 1
   465  		utxoView.DisconnectTransaction(
   466  			updateGlobalParamsTxn, updateGlobalParamsTxn.Hash(), utxoOps, blockHeight)
   467  
   468  		require.NoError(utxoView.FlushToDb())
   469  
   470  		require.Equal(DbGetGlobalParamsEntry(utxoView.Handle), prevGlobalParams)
   471  		require.Equal(utxoView.GlobalParamsEntry, prevGlobalParams)
   472  
   473  		// Check the balance of the updater after this txn
   474  		require.NotEqual(0, _getBalance(t, chain, nil, moneyPkString))
   475  	}
   476  }
   477  
   478  func TestBasicTransfer(t *testing.T) {
   479  	assert := assert.New(t)
   480  	require := require.New(t)
   481  	_ = assert
   482  	_ = require
   483  
   484  	chain, params, db := NewLowDifficultyBlockchain()
   485  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
   486  	// Mine two blocks to give the sender some DeSo.
   487  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
   488  	require.NoError(err)
   489  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
   490  	require.NoError(err)
   491  
   492  	senderPkBytes, _, err := Base58CheckDecode(senderPkString)
   493  	require.NoError(err)
   494  	recipientPkBytes, _, err := Base58CheckDecode(recipientPkString)
   495  	require.NoError(err)
   496  
   497  	// A basic transfer whose input public keys differ from the
   498  	// transaction-level public key should fail.
   499  	{
   500  		txn := &MsgDeSoTxn{
   501  			// The inputs will be set below.
   502  			TxInputs: []*DeSoInput{},
   503  			TxOutputs: []*DeSoOutput{
   504  				{
   505  					PublicKey:   recipientPkBytes,
   506  					AmountNanos: 1,
   507  				},
   508  			},
   509  			PublicKey: senderPkBytes,
   510  			TxnMeta:   &BasicTransferMetadata{},
   511  		}
   512  
   513  		totalInput, spendAmount, changeAmount, fees, err :=
   514  			chain.AddInputsAndChangeToTransaction(txn, 10, nil)
   515  		require.NoError(err)
   516  		require.Equal(totalInput, spendAmount+changeAmount+fees)
   517  		require.Greater(totalInput, uint64(0))
   518  
   519  		// At this point the txn has inputs for senderPkString. Change
   520  		// the public key to recipientPkString and sign it with the
   521  		// recipientPrivString.
   522  		txn.PublicKey = recipientPkBytes
   523  
   524  		_signTxn(t, txn, recipientPrivString)
   525  		utxoView, _ := NewUtxoView(db, params, nil)
   526  		txHash := txn.Hash()
   527  		blockHeight := chain.blockTip().Height + 1
   528  		_, _, _, _, err =
   529  			utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight,
   530  				true /*verifySignatures*/, false /*ignoreUtxos*/)
   531  		require.Error(err)
   532  		require.Contains(err.Error(), RuleErrorInputWithPublicKeyDifferentFromTxnPublicKey)
   533  	}
   534  
   535  	// Just a basic transfer with a bad signature.
   536  	{
   537  		txn := &MsgDeSoTxn{
   538  			// The inputs will be set below.
   539  			TxInputs: []*DeSoInput{},
   540  			TxOutputs: []*DeSoOutput{
   541  				{
   542  					PublicKey:   recipientPkBytes,
   543  					AmountNanos: 1,
   544  				},
   545  			},
   546  			PublicKey: senderPkBytes,
   547  			TxnMeta:   &BasicTransferMetadata{},
   548  		}
   549  
   550  		totalInput, spendAmount, changeAmount, fees, err :=
   551  			chain.AddInputsAndChangeToTransaction(txn, 10, nil)
   552  		require.NoError(err)
   553  		require.Equal(totalInput, spendAmount+changeAmount+fees)
   554  		require.Greater(totalInput, uint64(0))
   555  
   556  		// Sign the transaction with the recipient's key rather than the
   557  		// sender's key.
   558  		_signTxn(t, txn, recipientPrivString)
   559  		utxoView, _ := NewUtxoView(db, params, nil)
   560  		txHash := txn.Hash()
   561  		blockHeight := chain.blockTip().Height + 1
   562  		_, _, _, _, err =
   563  			utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight,
   564  				true /*verifySignature*/, false /*ignoreUtxos*/)
   565  		require.Error(err)
   566  		require.Contains(err.Error(), RuleErrorInvalidTransactionSignature)
   567  	}
   568  
   569  	// A block reward with a bad signature should fail.
   570  	{
   571  		txn := &MsgDeSoTxn{
   572  			// The inputs will be set below.
   573  			TxInputs: []*DeSoInput{},
   574  			TxOutputs: []*DeSoOutput{
   575  				{
   576  					PublicKey:   recipientPkBytes,
   577  					AmountNanos: 1,
   578  				},
   579  			},
   580  			PublicKey: senderPkBytes,
   581  			TxnMeta: &BlockRewardMetadataa{
   582  				ExtraData: []byte{0x00, 0x01},
   583  			},
   584  		}
   585  		_signTxn(t, txn, senderPrivString)
   586  		utxoView, _ := NewUtxoView(db, params, nil)
   587  		txHash := txn.Hash()
   588  		blockHeight := chain.blockTip().Height + 1
   589  		_, _, _, _, err =
   590  			utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight,
   591  				true /*verifySignature*/, false /*ignoreUtxos*/)
   592  		require.Error(err)
   593  		require.Contains(err.Error(), RuleErrorBlockRewardTxnNotAllowedToHaveSignature)
   594  	}
   595  
   596  	// A block reward with an input, even if it's signed legitimately,
   597  	// should fail.
   598  	{
   599  		txn := &MsgDeSoTxn{
   600  			// The inputs will be set below.
   601  			TxInputs: []*DeSoInput{},
   602  			TxOutputs: []*DeSoOutput{
   603  				{
   604  					PublicKey:   recipientPkBytes,
   605  					AmountNanos: 1,
   606  				},
   607  			},
   608  			PublicKey: senderPkBytes,
   609  			TxnMeta: &BlockRewardMetadataa{
   610  				ExtraData: []byte{0x00, 0x01},
   611  			},
   612  		}
   613  
   614  		totalInput, spendAmount, changeAmount, fees, err :=
   615  			chain.AddInputsAndChangeToTransaction(txn, 10, nil)
   616  		require.NoError(err)
   617  		require.Equal(totalInput, spendAmount+changeAmount+fees)
   618  		require.Greater(totalInput, uint64(0))
   619  
   620  		_signTxn(t, txn, senderPrivString)
   621  		utxoView, _ := NewUtxoView(db, params, nil)
   622  		txHash := txn.Hash()
   623  		blockHeight := chain.blockTip().Height + 1
   624  		_, _, _, _, err =
   625  			utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight,
   626  				true /*verifySignature*/, false /*ignoreUtxos*/)
   627  		require.Error(err)
   628  		require.Contains(err.Error(), RuleErrorBlockRewardTxnNotAllowedToHaveInputs)
   629  	}
   630  
   631  	// A block with too much block reward should fail.
   632  	allowedBlockReward := CalcBlockRewardNanos(chain.blockTip().Height)
   633  	assert.Equal(int64(allowedBlockReward), int64(1*NanosPerUnit))
   634  	blockToMine, _, _, err := miner._getBlockToMine(0 /*threadIndex*/)
   635  	require.NoError(err)
   636  	{
   637  		blockToMine.Txns[0].TxOutputs[0].AmountNanos = allowedBlockReward + 1
   638  		// One iteration should be sufficient to find us a good block.
   639  		_, bestNonce, err := FindLowestHash(blockToMine.Header, 10000)
   640  		require.NoError(err)
   641  		blockToMine.Header.Nonce = bestNonce
   642  
   643  		txHashes, err := ComputeTransactionHashes(blockToMine.Txns)
   644  		require.NoError(err)
   645  		utxoView, _ := NewUtxoView(db, params, nil)
   646  		_, err = utxoView.ConnectBlock(blockToMine, txHashes, true /*verifySignatures*/, nil)
   647  		require.Error(err)
   648  		require.Contains(err.Error(), RuleErrorBlockRewardExceedsMaxAllowed)
   649  	}
   650  
   651  	// A block with less than the max block reward should be OK.
   652  	{
   653  		blockToMine.Txns[0].TxOutputs[0].AmountNanos = allowedBlockReward - 1
   654  		// One iteration should be sufficient to find us a good block.
   655  		_, bestNonce, err := FindLowestHash(blockToMine.Header, 10000)
   656  		require.NoError(err)
   657  		blockToMine.Header.Nonce = bestNonce
   658  
   659  		txHashes, err := ComputeTransactionHashes(blockToMine.Txns)
   660  		require.NoError(err)
   661  		utxoView, _ := NewUtxoView(db, params, nil)
   662  		_, err = utxoView.ConnectBlock(blockToMine, txHashes, true /*verifySignatures*/, nil)
   663  		require.NoError(err)
   664  	}
   665  }