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

     1  package lib
     2  
     3  import (
     4  	"github.com/dgraph-io/badger/v3"
     5  	"github.com/stretchr/testify/assert"
     6  	"github.com/stretchr/testify/require"
     7  	"math"
     8  	"reflect"
     9  	"testing"
    10  )
    11  
    12  func _createNFT(t *testing.T, chain *Blockchain, db *badger.DB, params *DeSoParams,
    13  	feeRateNanosPerKB uint64, updaterPkBase58Check string, updaterPrivBase58Check string,
    14  	nftPostHash *BlockHash, numCopies uint64, hasUnlockable bool, isForSale bool, minBidAmountNanos uint64,
    15  	nftFee uint64, nftRoyaltyToCreatorBasisPoints uint64, nftRoyaltyToCoinBasisPoints uint64,
    16  ) (_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
    17  
    18  	assert := assert.New(t)
    19  	require := require.New(t)
    20  	_ = assert
    21  	_ = require
    22  
    23  	updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check)
    24  	require.NoError(err)
    25  
    26  	utxoView, err := NewUtxoView(db, params, nil)
    27  	require.NoError(err)
    28  
    29  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateCreateNFTTxn(
    30  		updaterPkBytes,
    31  		nftPostHash,
    32  		numCopies,
    33  		hasUnlockable,
    34  		isForSale,
    35  		minBidAmountNanos,
    36  		nftFee,
    37  		nftRoyaltyToCreatorBasisPoints,
    38  		nftRoyaltyToCoinBasisPoints,
    39  		feeRateNanosPerKB,
    40  		nil, []*DeSoOutput{})
    41  	if err != nil {
    42  		return nil, nil, 0, err
    43  	}
    44  
    45  	// Note: the "nftFee" is the "spendAmount" and therefore must be added to feesMake.
    46  	require.Equal(totalInputMake, changeAmountMake+feesMake+nftFee)
    47  
    48  	// Sign the transaction now that its inputs are set up.
    49  	_signTxn(t, txn, updaterPrivBase58Check)
    50  
    51  	txHash := txn.Hash()
    52  	// Always use height+1 for validation since it's assumed the transaction will
    53  	// get mined into the next block.
    54  	blockHeight := chain.blockTip().Height + 1
    55  	utxoOps, totalInput, totalOutput, fees, err :=
    56  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
    57  	if err != nil {
    58  		return nil, nil, 0, err
    59  	}
    60  	require.Equal(totalInput, totalOutput+fees)
    61  	require.Equal(totalInput, totalInputMake)
    62  
    63  	// We should have one SPEND UtxoOperation for each input, one ADD operation
    64  	// for each output, and one OperationTypeCreateNFT operation at the end.
    65  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
    66  	for ii := 0; ii < len(txn.TxInputs); ii++ {
    67  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
    68  	}
    69  	require.Equal(OperationTypeCreateNFT, utxoOps[len(utxoOps)-1].Type)
    70  
    71  	require.NoError(utxoView.FlushToDb())
    72  
    73  	return utxoOps, txn, blockHeight, nil
    74  }
    75  
    76  func _createNFTWithTestMeta(
    77  	testMeta *TestMeta,
    78  	feeRateNanosPerKB uint64,
    79  	updaterPkBase58Check string,
    80  	updaterPrivBase58Check string,
    81  	postHashToModify *BlockHash,
    82  	numCopies uint64,
    83  	hasUnlockable bool,
    84  	isForSale bool,
    85  	minBidAmountNanos uint64,
    86  	nftFee uint64,
    87  	nftRoyaltyToCreatorBasisPoints uint64,
    88  	nftRoyaltyToCoinBasisPoints uint64,
    89  ) {
    90  	// Sanity check: the number of NFT entries before should be 0.
    91  	dbNFTEntries := DBGetNFTEntriesForPostHash(testMeta.db, postHashToModify)
    92  	require.Equal(testMeta.t, 0, len(dbNFTEntries))
    93  
    94  	testMeta.expectedSenderBalances = append(
    95  		testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check))
    96  	currentOps, currentTxn, _, err := _createNFT(
    97  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB,
    98  		updaterPkBase58Check,
    99  		updaterPrivBase58Check,
   100  		postHashToModify,
   101  		numCopies,
   102  		hasUnlockable,
   103  		isForSale,
   104  		minBidAmountNanos,
   105  		nftFee,
   106  		nftRoyaltyToCreatorBasisPoints,
   107  		nftRoyaltyToCoinBasisPoints,
   108  	)
   109  	require.NoError(testMeta.t, err)
   110  
   111  	// Sanity check: the number of NFT entries after should be numCopies.
   112  	dbNFTEntries = DBGetNFTEntriesForPostHash(testMeta.db, postHashToModify)
   113  	require.Equal(testMeta.t, int(numCopies), len(dbNFTEntries))
   114  
   115  	// Sanity check that the first entry has serial number 1.
   116  	require.Equal(testMeta.t, uint64(1), dbNFTEntries[0].SerialNumber)
   117  
   118  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   119  	testMeta.txns = append(testMeta.txns, currentTxn)
   120  }
   121  
   122  func _createNFTBid(t *testing.T, chain *Blockchain, db *badger.DB, params *DeSoParams,
   123  	feeRateNanosPerKB uint64, updaterPkBase58Check string, updaterPrivBase58Check string,
   124  	nftPostHash *BlockHash, serialNumber uint64, bidAmountNanos uint64,
   125  ) (_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
   126  
   127  	assert := assert.New(t)
   128  	require := require.New(t)
   129  	_ = assert
   130  	_ = require
   131  
   132  	updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check)
   133  	require.NoError(err)
   134  
   135  	utxoView, err := NewUtxoView(db, params, nil)
   136  	require.NoError(err)
   137  
   138  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateNFTBidTxn(
   139  		updaterPkBytes,
   140  		nftPostHash,
   141  		serialNumber,
   142  		bidAmountNanos,
   143  		feeRateNanosPerKB,
   144  		nil,
   145  		[]*DeSoOutput{})
   146  	if err != nil {
   147  		return nil, nil, 0, err
   148  	}
   149  
   150  	require.Equal(totalInputMake, changeAmountMake+feesMake)
   151  
   152  	// Sign the transaction now that its inputs are set up.
   153  	_signTxn(t, txn, updaterPrivBase58Check)
   154  
   155  	txHash := txn.Hash()
   156  	// Always use height+1 for validation since it's assumed the transaction will
   157  	// get mined into the next block.
   158  	blockHeight := chain.blockTip().Height + 1
   159  	utxoOps, totalInput, totalOutput, fees, err :=
   160  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   161  	if err != nil {
   162  		return nil, nil, 0, err
   163  	}
   164  	require.Equal(totalInput, totalOutput+fees)
   165  	require.Equal(totalInput, totalInputMake)
   166  
   167  	// We should have one SPEND UtxoOperation for each input, one ADD operation
   168  	// for each output, and one OperationTypeNFTBid operation at the end.
   169  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
   170  	for ii := 0; ii < len(txn.TxInputs); ii++ {
   171  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
   172  	}
   173  	require.Equal(OperationTypeNFTBid, utxoOps[len(utxoOps)-1].Type)
   174  
   175  	require.NoError(utxoView.FlushToDb())
   176  
   177  	return utxoOps, txn, blockHeight, nil
   178  }
   179  
   180  func _createNFTBidWithTestMeta(
   181  	testMeta *TestMeta,
   182  	feeRateNanosPerKB uint64,
   183  	updaterPkBase58Check string,
   184  	updaterPrivBase58Check string,
   185  	postHash *BlockHash,
   186  	serialNumber uint64,
   187  	bidAmountNanos uint64,
   188  ) {
   189  	testMeta.expectedSenderBalances = append(
   190  		testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check))
   191  	currentOps, currentTxn, _, err := _createNFTBid(
   192  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB,
   193  		updaterPkBase58Check,
   194  		updaterPrivBase58Check,
   195  		postHash,
   196  		serialNumber,
   197  		bidAmountNanos,
   198  	)
   199  	require.NoError(testMeta.t, err)
   200  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   201  	testMeta.txns = append(testMeta.txns, currentTxn)
   202  }
   203  
   204  func _acceptNFTBid(t *testing.T, chain *Blockchain, db *badger.DB, params *DeSoParams,
   205  	feeRateNanosPerKB uint64, updaterPkBase58Check string, updaterPrivBase58Check string, nftPostHash *BlockHash,
   206  	serialNumber uint64, bidderPkBase58Check string, bidAmountNanos uint64, unencryptedUnlockableText string,
   207  ) (_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
   208  
   209  	assert := assert.New(t)
   210  	require := require.New(t)
   211  	_ = assert
   212  	_ = require
   213  
   214  	updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check)
   215  	require.NoError(err)
   216  
   217  	bidderPkBytes, _, err := Base58CheckDecode(bidderPkBase58Check)
   218  	require.NoError(err)
   219  
   220  	utxoView, err := NewUtxoView(db, params, nil)
   221  	require.NoError(err)
   222  
   223  	bidderPKID := utxoView.GetPKIDForPublicKey(bidderPkBytes)
   224  	require.NotNil(bidderPKID)
   225  	require.False(bidderPKID.isDeleted)
   226  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateAcceptNFTBidTxn(
   227  		updaterPkBytes,
   228  		nftPostHash,
   229  		serialNumber,
   230  		bidderPKID.PKID,
   231  		bidAmountNanos,
   232  		[]byte(unencryptedUnlockableText),
   233  		feeRateNanosPerKB,
   234  		nil,
   235  		[]*DeSoOutput{})
   236  	if err != nil {
   237  		return nil, nil, 0, err
   238  	}
   239  
   240  	require.Equal(totalInputMake, changeAmountMake+feesMake)
   241  
   242  	// Sign the transaction now that its inputs are set up.
   243  	_signTxn(t, txn, updaterPrivBase58Check)
   244  
   245  	txHash := txn.Hash()
   246  	// Always use height+1 for validation since it's assumed the transaction will
   247  	// get mined into the next block.
   248  	blockHeight := chain.blockTip().Height + 1
   249  	utxoOps, totalInput, totalOutput, fees, err :=
   250  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   251  	if err != nil {
   252  		return nil, nil, 0, err
   253  	}
   254  	require.Equal(totalInput, totalOutput+fees)
   255  	require.Equal(totalInput, totalInputMake)
   256  
   257  	// We should have one SPEND UtxoOperation for each input, one SPEND
   258  	// operation for each BidderInpout, one ADD operation
   259  	// for each output, and one OperationTypeAcceptNFTBid operation at the end.
   260  	numInputs := len(txn.TxInputs) + len(txn.TxnMeta.(*AcceptNFTBidMetadata).BidderInputs)
   261  	numOps := len(utxoOps)
   262  	for ii := 0; ii < numInputs; ii++ {
   263  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
   264  	}
   265  	for ii := numInputs; ii < numOps-1; ii++ {
   266  		require.Equal(OperationTypeAddUtxo, utxoOps[ii].Type)
   267  	}
   268  	require.Equal(OperationTypeAcceptNFTBid, utxoOps[numOps-1].Type)
   269  
   270  	require.NoError(utxoView.FlushToDb())
   271  
   272  	return utxoOps, txn, blockHeight, nil
   273  }
   274  
   275  func _acceptNFTBidWithTestMeta(
   276  	testMeta *TestMeta,
   277  	feeRateNanosPerKB uint64,
   278  	updaterPkBase58Check string,
   279  	updaterPrivBase58Check string,
   280  	postHash *BlockHash,
   281  	serialNumber uint64,
   282  	bidderPkBase58Check string,
   283  	bidAmountNanos uint64,
   284  	unencryptedUnlockableText string,
   285  ) {
   286  	testMeta.expectedSenderBalances = append(
   287  		testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check))
   288  	currentOps, currentTxn, _, err := _acceptNFTBid(
   289  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB,
   290  		updaterPkBase58Check,
   291  		updaterPrivBase58Check,
   292  		postHash,
   293  		serialNumber,
   294  		bidderPkBase58Check,
   295  		bidAmountNanos,
   296  		unencryptedUnlockableText,
   297  	)
   298  	require.NoError(testMeta.t, err)
   299  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   300  	testMeta.txns = append(testMeta.txns, currentTxn)
   301  }
   302  
   303  func _updateNFT(t *testing.T, chain *Blockchain, db *badger.DB, params *DeSoParams,
   304  	feeRateNanosPerKB uint64, updaterPkBase58Check string, updaterPrivBase58Check string,
   305  	nftPostHash *BlockHash, serialNumber uint64, isForSale bool, minBidAmountNanos uint64,
   306  ) (_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
   307  
   308  	assert := assert.New(t)
   309  	require := require.New(t)
   310  	_ = assert
   311  	_ = require
   312  
   313  	updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check)
   314  	require.NoError(err)
   315  
   316  	utxoView, err := NewUtxoView(db, params, nil)
   317  	require.NoError(err)
   318  
   319  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateUpdateNFTTxn(
   320  		updaterPkBytes,
   321  		nftPostHash,
   322  		serialNumber,
   323  		isForSale,
   324  		minBidAmountNanos,
   325  		feeRateNanosPerKB,
   326  		nil,
   327  		[]*DeSoOutput{})
   328  	if err != nil {
   329  		return nil, nil, 0, err
   330  	}
   331  
   332  	require.Equal(totalInputMake, changeAmountMake+feesMake)
   333  
   334  	// Sign the transaction now that its inputs are set up.
   335  	_signTxn(t, txn, updaterPrivBase58Check)
   336  
   337  	txHash := txn.Hash()
   338  	// Always use height+1 for validation since it's assumed the transaction will
   339  	// get mined into the next block.
   340  	blockHeight := chain.blockTip().Height + 1
   341  	utxoOps, totalInput, totalOutput, fees, err :=
   342  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   343  	if err != nil {
   344  		return nil, nil, 0, err
   345  	}
   346  	require.Equal(totalInput, totalOutput+fees)
   347  	require.Equal(totalInput, totalInputMake)
   348  
   349  	// We should have one SPEND UtxoOperation for each input, one ADD operation
   350  	// for each output, and one OperationTypeUpdateNFT operation at the end.
   351  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
   352  	for ii := 0; ii < len(txn.TxInputs); ii++ {
   353  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
   354  	}
   355  	require.Equal(OperationTypeUpdateNFT, utxoOps[len(utxoOps)-1].Type)
   356  
   357  	require.NoError(utxoView.FlushToDb())
   358  
   359  	return utxoOps, txn, blockHeight, nil
   360  }
   361  
   362  func _updateNFTWithTestMeta(
   363  	testMeta *TestMeta,
   364  	feeRateNanosPerKB uint64,
   365  	updaterPkBase58Check string,
   366  	updaterPrivBase58Check string,
   367  	postHash *BlockHash,
   368  	serialNumber uint64,
   369  	isForSale bool,
   370  	minBidAmountNanos uint64,
   371  ) {
   372  	testMeta.expectedSenderBalances = append(
   373  		testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check))
   374  	currentOps, currentTxn, _, err := _updateNFT(
   375  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB,
   376  		updaterPkBase58Check,
   377  		updaterPrivBase58Check,
   378  		postHash,
   379  		serialNumber,
   380  		isForSale,
   381  		minBidAmountNanos,
   382  	)
   383  	require.NoError(testMeta.t, err)
   384  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   385  	testMeta.txns = append(testMeta.txns, currentTxn)
   386  }
   387  
   388  func _transferNFT(t *testing.T, chain *Blockchain, db *badger.DB, params *DeSoParams,
   389  	feeRateNanosPerKB uint64, senderPk string, senderPriv string, receiverPk string,
   390  	nftPostHash *BlockHash, serialNumber uint64, unlockableText string,
   391  ) (_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
   392  
   393  	assert := assert.New(t)
   394  	require := require.New(t)
   395  	_ = assert
   396  	_ = require
   397  
   398  	senderPkBytes, _, err := Base58CheckDecode(senderPk)
   399  	require.NoError(err)
   400  
   401  	receiverPkBytes, _, err := Base58CheckDecode(receiverPk)
   402  	require.NoError(err)
   403  
   404  	utxoView, err := NewUtxoView(db, params, nil)
   405  	require.NoError(err)
   406  
   407  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateNFTTransferTxn(
   408  		senderPkBytes,
   409  		receiverPkBytes,
   410  		nftPostHash,
   411  		serialNumber,
   412  		[]byte(unlockableText),
   413  		feeRateNanosPerKB,
   414  		nil,
   415  		[]*DeSoOutput{})
   416  	if err != nil {
   417  		return nil, nil, 0, err
   418  	}
   419  
   420  	require.Equal(totalInputMake, changeAmountMake+feesMake)
   421  
   422  	// Sign the transaction now that its inputs are set up.
   423  	_signTxn(t, txn, senderPriv)
   424  
   425  	txHash := txn.Hash()
   426  	// Always use height+1 for validation since it's assumed the transaction will
   427  	// get mined into the next block.
   428  	blockHeight := chain.blockTip().Height + 1
   429  	utxoOps, totalInput, totalOutput, fees, err :=
   430  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   431  	if err != nil {
   432  		return nil, nil, 0, err
   433  	}
   434  	require.Equal(totalInput, totalOutput+fees)
   435  	require.Equal(totalInput, totalInputMake)
   436  
   437  	// We should have one SPEND UtxoOperation for each input, one ADD operation
   438  	// for each output, and one OperationTypeNFTTransfer operation at the end.
   439  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
   440  	for ii := 0; ii < len(txn.TxInputs); ii++ {
   441  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
   442  	}
   443  	require.Equal(OperationTypeNFTTransfer, utxoOps[len(utxoOps)-1].Type)
   444  
   445  	require.NoError(utxoView.FlushToDb())
   446  
   447  	return utxoOps, txn, blockHeight, nil
   448  }
   449  
   450  func _transferNFTWithTestMeta(
   451  	testMeta *TestMeta,
   452  	feeRateNanosPerKB uint64,
   453  	senderPkBase58Check string,
   454  	senderPrivBase58Check string,
   455  	receiverPkBase58Check string,
   456  	postHash *BlockHash,
   457  	serialNumber uint64,
   458  	unlockableText string,
   459  ) {
   460  	testMeta.expectedSenderBalances = append(
   461  		testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, senderPkBase58Check))
   462  	currentOps, currentTxn, _, err := _transferNFT(
   463  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB,
   464  		senderPkBase58Check,
   465  		senderPrivBase58Check,
   466  		receiverPkBase58Check,
   467  		postHash,
   468  		serialNumber,
   469  		unlockableText,
   470  	)
   471  	require.NoError(testMeta.t, err)
   472  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   473  	testMeta.txns = append(testMeta.txns, currentTxn)
   474  }
   475  
   476  func _acceptNFTTransfer(t *testing.T, chain *Blockchain, db *badger.DB,
   477  	params *DeSoParams, feeRateNanosPerKB uint64, updaterPkBase58Check string,
   478  	updaterPrivBase58Check string, nftPostHash *BlockHash, serialNumber uint64,
   479  ) (_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
   480  
   481  	assert := assert.New(t)
   482  	require := require.New(t)
   483  	_ = assert
   484  	_ = require
   485  
   486  	updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check)
   487  	require.NoError(err)
   488  
   489  	utxoView, err := NewUtxoView(db, params, nil)
   490  	require.NoError(err)
   491  
   492  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateAcceptNFTTransferTxn(
   493  		updaterPkBytes,
   494  		nftPostHash,
   495  		serialNumber,
   496  		feeRateNanosPerKB,
   497  		nil,
   498  		[]*DeSoOutput{})
   499  	if err != nil {
   500  		return nil, nil, 0, err
   501  	}
   502  
   503  	require.Equal(totalInputMake, changeAmountMake+feesMake)
   504  
   505  	// Sign the transaction now that its inputs are set up.
   506  	_signTxn(t, txn, updaterPrivBase58Check)
   507  
   508  	txHash := txn.Hash()
   509  	// Always use height+1 for validation since it's assumed the transaction will
   510  	// get mined into the next block.
   511  	blockHeight := chain.blockTip().Height + 1
   512  	utxoOps, totalInput, totalOutput, fees, err :=
   513  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   514  	if err != nil {
   515  		return nil, nil, 0, err
   516  	}
   517  	require.Equal(totalInput, totalOutput+fees)
   518  	require.Equal(totalInput, totalInputMake)
   519  
   520  	// We should have one SPEND UtxoOperation for each input, one ADD operation
   521  	// for each output, and one OperationTypeNFTTransfer operation at the end.
   522  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
   523  	for ii := 0; ii < len(txn.TxInputs); ii++ {
   524  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
   525  	}
   526  	require.Equal(OperationTypeAcceptNFTTransfer, utxoOps[len(utxoOps)-1].Type)
   527  
   528  	require.NoError(utxoView.FlushToDb())
   529  
   530  	return utxoOps, txn, blockHeight, nil
   531  }
   532  
   533  func _acceptNFTTransferWithTestMeta(
   534  	testMeta *TestMeta,
   535  	feeRateNanosPerKB uint64,
   536  	updaterPkBase58Check string,
   537  	updaterPrivBase58Check string,
   538  	postHash *BlockHash,
   539  	serialNumber uint64,
   540  ) {
   541  	testMeta.expectedSenderBalances = append(
   542  		testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check))
   543  	currentOps, currentTxn, _, err := _acceptNFTTransfer(
   544  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB,
   545  		updaterPkBase58Check,
   546  		updaterPrivBase58Check,
   547  		postHash,
   548  		serialNumber,
   549  	)
   550  	require.NoError(testMeta.t, err)
   551  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   552  	testMeta.txns = append(testMeta.txns, currentTxn)
   553  }
   554  
   555  func _burnNFT(t *testing.T, chain *Blockchain, db *badger.DB,
   556  	params *DeSoParams, feeRateNanosPerKB uint64, updaterPkBase58Check string,
   557  	updaterPrivBase58Check string, nftPostHash *BlockHash, serialNumber uint64,
   558  ) (_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) {
   559  
   560  	assert := assert.New(t)
   561  	require := require.New(t)
   562  	_ = assert
   563  	_ = require
   564  
   565  	updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check)
   566  	require.NoError(err)
   567  
   568  	utxoView, err := NewUtxoView(db, params, nil)
   569  	require.NoError(err)
   570  
   571  	txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateBurnNFTTxn(
   572  		updaterPkBytes,
   573  		nftPostHash,
   574  		serialNumber,
   575  		feeRateNanosPerKB,
   576  		nil,
   577  		[]*DeSoOutput{})
   578  	if err != nil {
   579  		return nil, nil, 0, err
   580  	}
   581  
   582  	require.Equal(totalInputMake, changeAmountMake+feesMake)
   583  
   584  	// Sign the transaction now that its inputs are set up.
   585  	_signTxn(t, txn, updaterPrivBase58Check)
   586  
   587  	txHash := txn.Hash()
   588  	// Always use height+1 for validation since it's assumed the transaction will
   589  	// get mined into the next block.
   590  	blockHeight := chain.blockTip().Height + 1
   591  	utxoOps, totalInput, totalOutput, fees, err :=
   592  		utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/)
   593  	if err != nil {
   594  		return nil, nil, 0, err
   595  	}
   596  	require.Equal(totalInput, totalOutput+fees)
   597  	require.Equal(totalInput, totalInputMake)
   598  
   599  	// We should have one SPEND UtxoOperation for each input, one ADD operation
   600  	// for each output, and one OperationTypeNFTTransfer operation at the end.
   601  	require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps))
   602  	for ii := 0; ii < len(txn.TxInputs); ii++ {
   603  		require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type)
   604  	}
   605  	require.Equal(OperationTypeBurnNFT, utxoOps[len(utxoOps)-1].Type)
   606  
   607  	require.NoError(utxoView.FlushToDb())
   608  
   609  	return utxoOps, txn, blockHeight, nil
   610  }
   611  
   612  func _burnNFTWithTestMeta(
   613  	testMeta *TestMeta,
   614  	feeRateNanosPerKB uint64,
   615  	updaterPkBase58Check string,
   616  	updaterPrivBase58Check string,
   617  	postHash *BlockHash,
   618  	serialNumber uint64,
   619  ) {
   620  	testMeta.expectedSenderBalances = append(
   621  		testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check))
   622  	currentOps, currentTxn, _, err := _burnNFT(
   623  		testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB,
   624  		updaterPkBase58Check,
   625  		updaterPrivBase58Check,
   626  		postHash,
   627  		serialNumber,
   628  	)
   629  	require.NoError(testMeta.t, err)
   630  	testMeta.txnOps = append(testMeta.txnOps, currentOps)
   631  	testMeta.txns = append(testMeta.txns, currentTxn)
   632  }
   633  
   634  func TestNFTBasic(t *testing.T) {
   635  	BrokenNFTBidsFixBlockHeight = uint32(0)
   636  
   637  	assert := assert.New(t)
   638  	require := require.New(t)
   639  	_ = assert
   640  	_ = require
   641  
   642  	chain, params, db := NewLowDifficultyBlockchain()
   643  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
   644  	// Make m3, m4 a paramUpdater for this test
   645  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
   646  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
   647  
   648  	// Mine a few blocks to give the senderPkString some money.
   649  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
   650  	require.NoError(err)
   651  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
   652  	require.NoError(err)
   653  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
   654  	require.NoError(err)
   655  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
   656  	require.NoError(err)
   657  
   658  	// We build the testMeta obj after mining blocks so that we save the correct block height.
   659  	testMeta := &TestMeta{
   660  		t:           t,
   661  		chain:       chain,
   662  		params:      params,
   663  		db:          db,
   664  		mempool:     mempool,
   665  		miner:       miner,
   666  		savedHeight: chain.blockTip().Height + 1,
   667  	}
   668  
   669  	// Fund all the keys.
   670  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 70)
   671  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 420)
   672  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 140)
   673  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 210)
   674  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
   675  
   676  	// Set max copies to a non-zero value to activate NFTs.
   677  	{
   678  		_updateGlobalParamsEntryWithTestMeta(
   679  			testMeta,
   680  			10, /*FeeRateNanosPerKB*/
   681  			m4Pub,
   682  			m4Priv,
   683  			-1, -1, -1, -1,
   684  			1000, /*maxCopiesPerNFT*/
   685  		)
   686  	}
   687  
   688  	// Create a simple post.
   689  	{
   690  		_submitPostWithTestMeta(
   691  			testMeta,
   692  			10,                                 /*feeRateNanosPerKB*/
   693  			m0Pub,                              /*updaterPkBase58Check*/
   694  			m0Priv,                             /*updaterPrivBase58Check*/
   695  			[]byte{},                           /*postHashToModify*/
   696  			[]byte{},                           /*parentStakeID*/
   697  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
   698  			[]byte{},
   699  			1502947011*1e9, /*tstampNanos*/
   700  			false /*isHidden*/)
   701  	}
   702  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
   703  
   704  	// Error case: can't make an NFT without a profile.
   705  	{
   706  		_, _, _, err := _createNFT(
   707  			t, chain, db, params, 10,
   708  			m0Pub,
   709  			m0Priv,
   710  			post1Hash,
   711  			100,   /*NumCopies*/
   712  			false, /*HasUnlockable*/
   713  			true,  /*IsForSale*/
   714  			0,     /*MinBidAmountNanos*/
   715  			0,     /*nftFee*/
   716  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   717  			0,     /*nftRoyaltyToCoinBasisPoints*/
   718  		)
   719  
   720  		require.Error(err)
   721  		require.Contains(err.Error(), RuleErrorCantCreateNFTWithoutProfileEntry)
   722  	}
   723  
   724  	// Create a profile so we can make an NFT.
   725  	{
   726  		_updateProfileWithTestMeta(
   727  			testMeta,
   728  			10,            /*feeRateNanosPerKB*/
   729  			m0Pub,         /*updaterPkBase58Check*/
   730  			m0Priv,        /*updaterPrivBase58Check*/
   731  			[]byte{},      /*profilePubKey*/
   732  			"m2",          /*newUsername*/
   733  			"i am the m2", /*newDescription*/
   734  			shortPic,      /*newProfilePic*/
   735  			10*100,        /*newCreatorBasisPoints*/
   736  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
   737  			false /*isHidden*/)
   738  	}
   739  
   740  	// Error case: m0 cannot turn a vanilla repost of their post into an NFT.
   741  	{
   742  		_submitPostWithTestMeta(
   743  			testMeta,
   744  			10,                /*feeRateNanosPerKB*/
   745  			m0Pub,             /*updaterPkBase58Check*/
   746  			m0Priv,            /*updaterPrivBase58Check*/
   747  			[]byte{},          /*postHashToModify*/
   748  			[]byte{},          /*parentStakeID*/
   749  			&DeSoBodySchema{}, /*body*/
   750  			post1Hash[:],      /*repostedPostHash*/
   751  			1502947011*1e9,    /*tstampNanos*/
   752  			false /*isHidden*/)
   753  
   754  		vanillaRepostPostHash := testMeta.txns[len(testMeta.txns)-1].Hash()
   755  		_, _, _, err := _createNFT(
   756  			t, chain, db, params, 10,
   757  			m0Pub,
   758  			m0Priv,
   759  			vanillaRepostPostHash,
   760  			100,   /*NumCopies*/
   761  			false, /*HasUnlockable*/
   762  			true,  /*IsForSale*/
   763  			0,     /*MinBidAmountNanos*/
   764  			0,     /*nftFee*/
   765  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   766  			0,     /*nftRoyaltyToCoinBasisPoints*/
   767  		)
   768  
   769  		require.Error(err)
   770  		require.Contains(err.Error(), RuleErrorCreateNFTOnVanillaRepost)
   771  	}
   772  
   773  	// Error case: m1 should not be able to turn m0's post into an NFT.
   774  	{
   775  		_, _, _, err := _createNFT(
   776  			t, chain, db, params, 10,
   777  			m1Pub,
   778  			m1Priv,
   779  			post1Hash,
   780  			100,   /*NumCopies*/
   781  			false, /*HasUnlockable*/
   782  			true,  /*IsForSale*/
   783  			0,     /*MinBidAmountNanos*/
   784  			0,     /*nftFee*/
   785  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   786  			0,     /*nftRoyaltyToCoinBasisPoints*/
   787  		)
   788  
   789  		require.Error(err)
   790  		require.Contains(err.Error(), RuleErrorCreateNFTMustBeCalledByPoster)
   791  	}
   792  
   793  	// Error case: m0 should not be able to make more than MaxCopiesPerNFT.
   794  	{
   795  		_, _, _, err := _createNFT(
   796  			t, chain, db, params, 10,
   797  			m0Pub,
   798  			m0Priv,
   799  			post1Hash,
   800  			1001,  /*NumCopies*/
   801  			false, /*HasUnlockable*/
   802  			true,  /*IsForSale*/
   803  			0,     /*MinBidAmountNanos*/
   804  			0,     /*nftFee*/
   805  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   806  			0,     /*nftRoyaltyToCoinBasisPoints*/
   807  		)
   808  
   809  		require.Error(err)
   810  		require.Contains(err.Error(), RuleErrorTooManyNFTCopies)
   811  	}
   812  
   813  	// Error case: m0 should not be able to make an NFT with zero copies.
   814  	{
   815  		_, _, _, err := _createNFT(
   816  			t, chain, db, params, 10,
   817  			m0Pub,
   818  			m0Priv,
   819  			post1Hash,
   820  			0,     /*NumCopies*/
   821  			false, /*HasUnlockable*/
   822  			true,  /*IsForSale*/
   823  			0,     /*MinBidAmountNanos*/
   824  			0,     /*nftFee*/
   825  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   826  			0,     /*nftRoyaltyToCoinBasisPoints*/
   827  		)
   828  
   829  		require.Error(err)
   830  		require.Contains(err.Error(), RuleErrorNFTMustHaveNonZeroCopies)
   831  	}
   832  
   833  	// Error case: non-existent post.
   834  	{
   835  
   836  		fakePostHash := &BlockHash{
   837  			0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
   838  			0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
   839  			0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}
   840  
   841  		_, _, _, err := _createNFT(
   842  			t, chain, db, params, 10,
   843  			m0Pub,
   844  			m0Priv,
   845  			fakePostHash,
   846  			1,     /*NumCopies*/
   847  			false, /*HasUnlockable*/
   848  			true,  /*IsForSale*/
   849  			0,     /*MinBidAmountNanos*/
   850  			0,     /*nftFee*/
   851  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   852  			0,     /*nftRoyaltyToCoinBasisPoints*/
   853  		)
   854  
   855  		require.Error(err)
   856  		require.Contains(err.Error(), RuleErrorCreateNFTOnNonexistentPost)
   857  	}
   858  
   859  	// Finally, have m0 turn post1 into an NFT. Woohoo!
   860  	{
   861  		// Balance before.
   862  		m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub)
   863  		require.Equal(uint64(28), m0BalBeforeNFT)
   864  
   865  		_createNFTWithTestMeta(
   866  			testMeta,
   867  			10, /*FeeRateNanosPerKB*/
   868  			m0Pub,
   869  			m0Priv,
   870  			post1Hash,
   871  			5,     /*NumCopies*/
   872  			false, /*HasUnlockable*/
   873  			true,  /*IsForSale*/
   874  			0,     /*MinBidAmountNanos*/
   875  			0,     /*nftFee*/
   876  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   877  			0,     /*nftRoyaltyToCoinBasisPoints*/
   878  		)
   879  
   880  		// Balance after. Since the default NFT fee is 0, m0 is only charged the nanos per kb fee.
   881  		m0BalAfterNFT := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
   882  		require.Equal(uint64(27), m0BalAfterNFT)
   883  	}
   884  
   885  	// Error case: cannot turn a post into an NFT twice.
   886  	{
   887  		_, _, _, err := _createNFT(
   888  			t, chain, db, params, 10,
   889  			m0Pub,
   890  			m0Priv,
   891  			post1Hash,
   892  			5,     /*NumCopies*/
   893  			false, /*HasUnlockable*/
   894  			true,  /*IsForSale*/
   895  			0,     /*MinBidAmountNanos*/
   896  			0,     /*nftFee*/
   897  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   898  			0,     /*nftRoyaltyToCoinBasisPoints*/
   899  		)
   900  		require.Error(err)
   901  		require.Contains(err.Error(), RuleErrorCreateNFTOnPostThatAlreadyIsNFT)
   902  	}
   903  
   904  	// Error case: cannot modify a post after it is NFTed.
   905  	{
   906  		_, _, _, err := _submitPost(
   907  			testMeta.t, testMeta.chain, testMeta.db, testMeta.params,
   908  			10,
   909  			m0Pub,
   910  			m0Priv,
   911  			post1Hash[:],
   912  			[]byte{},
   913  			&DeSoBodySchema{Body: "modified m0 post"},
   914  			[]byte{},
   915  			1502947011*1e9,
   916  			false)
   917  
   918  		require.Error(err)
   919  		require.Contains(err.Error(), RuleErrorSubmitPostCannotUpdateNFT)
   920  	}
   921  
   922  	// Now let's try adding a fee to creating NFT copies. This fee exists since creating
   923  	// n-copies of an NFT causes the chain to do n-times as much work.
   924  	{
   925  		_updateGlobalParamsEntryWithTestMeta(
   926  			testMeta,
   927  			10, /*FeeRateNanosPerKB*/
   928  			m3Pub,
   929  			m3Priv,
   930  			-1, -1, -1,
   931  			1,  /*createNFTFeeNanos*/
   932  			-1, /*maxCopiesPerNFT*/
   933  		)
   934  	}
   935  
   936  	// Have m0 create another post for us to NFTify.
   937  	{
   938  		_submitPostWithTestMeta(
   939  			testMeta,
   940  			10,                                 /*feeRateNanosPerKB*/
   941  			m0Pub,                              /*updaterPkBase58Check*/
   942  			m0Priv,                             /*updaterPrivBase58Check*/
   943  			[]byte{},                           /*postHashToModify*/
   944  			[]byte{},                           /*parentStakeID*/
   945  			&DeSoBodySchema{Body: "m0 post 2"}, /*body*/
   946  			[]byte{},
   947  			1502947012*1e9, /*tstampNanos*/
   948  			false /*isHidden*/)
   949  	}
   950  	post2Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
   951  
   952  	// Error case: creating an NFT without paying the nftFee should fail.
   953  	{
   954  		_, _, _, err := _createNFT(
   955  			t, chain, db, params, 10,
   956  			m0Pub,
   957  			m0Priv,
   958  			post2Hash,
   959  			1000,  /*NumCopies*/
   960  			false, /*HasUnlockable*/
   961  			true,  /*IsForSale*/
   962  			0,     /*MinBidAmountNanos*/
   963  			0,     /*nftFee*/
   964  			0,     /*nftRoyaltyToCreatorBasisPoints*/
   965  			0,     /*nftRoyaltyToCoinBasisPoints*/
   966  		)
   967  		require.Error(err)
   968  		require.Contains(err.Error(), RuleErrorCreateNFTWithInsufficientFunds)
   969  	}
   970  
   971  	// Creating an NFT with the correct NFT fee should succeed.
   972  	// This time set HasUnlockable to 'true'.
   973  	{
   974  		utxoView, err := NewUtxoView(db, params, nil)
   975  		require.NoError(err)
   976  
   977  		numCopies := uint64(10)
   978  		nftFee := utxoView.GlobalParamsEntry.CreateNFTFeeNanos * numCopies
   979  
   980  		m0BalBeforeNFT := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
   981  		require.Equal(uint64(26), m0BalBeforeNFT)
   982  
   983  		_createNFTWithTestMeta(
   984  			testMeta,
   985  			10, /*FeeRateNanosPerKB*/
   986  			m0Pub,
   987  			m0Priv,
   988  			post2Hash,
   989  			10,     /*NumCopies*/
   990  			true,   /*HasUnlockable*/
   991  			true,   /*IsForSale*/
   992  			0,      /*MinBidAmountNanos*/
   993  			nftFee, /*nftFee*/
   994  			0,      /*nftRoyaltyToCreatorBasisPoints*/
   995  			0,      /*nftRoyaltyToCoinBasisPoints*/
   996  		)
   997  
   998  		// Check that m0 was charged the correct nftFee.
   999  		m0BalAfterNFT := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
  1000  		require.Equal(uint64(25)-nftFee, m0BalAfterNFT)
  1001  	}
  1002  
  1003  	//
  1004  	// Bidding on NFTs
  1005  	//
  1006  
  1007  	// Error case: non-existent NFT.
  1008  	{
  1009  		fakePostHash := &BlockHash{
  1010  			0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  1011  			0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
  1012  			0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1}
  1013  
  1014  		_, _, _, err := _createNFTBid(
  1015  			t, chain, db, params, 10,
  1016  			m0Pub,
  1017  			m0Priv,
  1018  			fakePostHash,
  1019  			1,          /*SerialNumber*/
  1020  			1000000000, /*BidAmountNanos*/
  1021  		)
  1022  		require.Error(err)
  1023  		require.Contains(err.Error(), RuleErrorNFTBidOnNonExistentPost)
  1024  	}
  1025  
  1026  	// Have m0 create another post that has not been NFTed.
  1027  	{
  1028  		_submitPostWithTestMeta(
  1029  			testMeta,
  1030  			10,                                 /*feeRateNanosPerKB*/
  1031  			m0Pub,                              /*updaterPkBase58Check*/
  1032  			m0Priv,                             /*updaterPrivBase58Check*/
  1033  			[]byte{},                           /*postHashToModify*/
  1034  			[]byte{},                           /*parentStakeID*/
  1035  			&DeSoBodySchema{Body: "m0 post 3"}, /*body*/
  1036  			[]byte{},
  1037  			1502947013*1e9, /*tstampNanos*/
  1038  			false /*isHidden*/)
  1039  	}
  1040  	post3Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  1041  
  1042  	// Error case: cannot bid on a post that is not an NFT.
  1043  	{
  1044  		_, _, _, err := _createNFTBid(
  1045  			t, chain, db, params, 10,
  1046  			m1Pub,
  1047  			m1Priv,
  1048  			post3Hash,
  1049  			1,          /*SerialNumber*/
  1050  			1000000000, /*BidAmountNanos*/
  1051  		)
  1052  		require.Error(err)
  1053  		require.Contains(err.Error(), RuleErrorNFTBidOnPostThatIsNotAnNFT)
  1054  	}
  1055  
  1056  	// Error case: Bidding on a serial number that does not exist should fail (post1 has 5 copies).
  1057  	{
  1058  		_, _, _, err = _createNFTBid(
  1059  			t, chain, db, params, 10,
  1060  			m1Pub,
  1061  			m1Priv,
  1062  			post1Hash,
  1063  			6,          /*SerialNumber*/
  1064  			1000000000, /*BidAmountNanos*/
  1065  		)
  1066  		require.Error(err)
  1067  		require.Contains(err.Error(), RuleErrorNFTBidOnInvalidSerialNumber)
  1068  	}
  1069  
  1070  	// Error case: cannot make a bid with a sufficient deso balance to fill the bid.
  1071  	{
  1072  		_, _, _, err = _createNFTBid(
  1073  			t, chain, db, params, 10,
  1074  			m1Pub,
  1075  			m1Priv,
  1076  			post1Hash,
  1077  			1,          /*SerialNumber*/
  1078  			1000000000, /*BidAmountNanos*/
  1079  		)
  1080  		require.Error(err)
  1081  		require.Contains(err.Error(), RuleErrorInsufficientFundsForNFTBid)
  1082  	}
  1083  
  1084  	// Error case: m0 cannot bid on its own NFT.
  1085  	{
  1086  		_, _, _, err := _createNFTBid(
  1087  			t, chain, db, params, 10,
  1088  			m0Pub,
  1089  			m0Priv,
  1090  			post1Hash,
  1091  			1,          /*SerialNumber*/
  1092  			1000000000, /*BidAmountNanos*/
  1093  		)
  1094  		require.Error(err)
  1095  		require.Contains(err.Error(), RuleErrorNFTOwnerCannotBidOnOwnedNFT)
  1096  	}
  1097  
  1098  	// Have m1 and m2 bid on post #1 / serial #1.
  1099  	{
  1100  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  1101  		require.Equal(0, len(bidEntries))
  1102  
  1103  		_createNFTBidWithTestMeta(
  1104  			testMeta,
  1105  			10, /*FeeRateNanosPerKB*/
  1106  			m1Pub,
  1107  			m1Priv,
  1108  			post1Hash,
  1109  			1, /*SerialNumber*/
  1110  			1, /*BidAmountNanos*/
  1111  		)
  1112  
  1113  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  1114  		require.Equal(1, len(bidEntries))
  1115  
  1116  		_createNFTBidWithTestMeta(
  1117  			testMeta,
  1118  			10, /*FeeRateNanosPerKB*/
  1119  			m2Pub,
  1120  			m2Priv,
  1121  			post1Hash,
  1122  			1, /*SerialNumber*/
  1123  			2, /*BidAmountNanos*/
  1124  		)
  1125  
  1126  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  1127  		require.Equal(2, len(bidEntries))
  1128  	}
  1129  
  1130  	// Error case: m1 should not be able to accept or update m0's NFTs.
  1131  	{
  1132  		_, _, _, err := _updateNFT(
  1133  			t, chain, db, params, 10,
  1134  			m1Pub,
  1135  			m1Priv,
  1136  			post1Hash,
  1137  			1,     /*SerialNumber*/
  1138  			false, /*IsForSale*/
  1139  			0,     /*MinBidAmountNanos*/
  1140  		)
  1141  		require.Error(err)
  1142  		require.Contains(err.Error(), RuleErrorUpdateNFTByNonOwner)
  1143  
  1144  		// m1 trying to be sneaky by accepting their own bid.
  1145  		_, _, _, err = _acceptNFTBid(
  1146  			t, chain, db, params, 10,
  1147  			m1Pub,
  1148  			m1Priv,
  1149  			post1Hash,
  1150  			1, /*SerialNumber*/
  1151  			m1Pub,
  1152  			1,  /*BidAmountNanos*/
  1153  			"", /*UnlockableText*/
  1154  		)
  1155  		require.Error(err)
  1156  		require.Contains(err.Error(), RuleErrorAcceptNFTBidByNonOwner)
  1157  	}
  1158  
  1159  	// Error case: accepting a bid that does not match the bid entry.
  1160  	{
  1161  		// m0 trying to be sneaky by setting m1's bid amount to 100x.
  1162  		_, _, _, err = _acceptNFTBid(
  1163  			t, chain, db, params, 10,
  1164  			m0Pub,
  1165  			m0Priv,
  1166  			post1Hash,
  1167  			1, /*SerialNumber*/
  1168  			m1Pub,
  1169  			100, /*BidAmountNanos*/
  1170  			"",  /*UnlockableText*/
  1171  		)
  1172  		require.Error(err)
  1173  		require.Contains(err.Error(), RuleErrorAcceptedNFTBidAmountDoesNotMatch)
  1174  	}
  1175  
  1176  	// Error case: can't accept a non-existent bid.
  1177  	{
  1178  		// m3 has not bid on this NFT.
  1179  		_, _, _, err = _acceptNFTBid(
  1180  			t, chain, db, params, 10,
  1181  			m0Pub,
  1182  			m0Priv,
  1183  			post1Hash,
  1184  			1, /*SerialNumber*/
  1185  			m3Pub,
  1186  			200, /*BidAmountNanos*/
  1187  			"",  /*UnlockableText*/
  1188  		)
  1189  		require.Error(err)
  1190  		require.Contains(err.Error(), RuleErrorCantAcceptNonExistentBid)
  1191  	}
  1192  
  1193  	// Error case: can't accept or update a non-existent NFT.
  1194  	{
  1195  		_, _, _, err := _updateNFT(
  1196  			t, chain, db, params, 10,
  1197  			m0Pub,
  1198  			m0Priv,
  1199  			post1Hash,
  1200  			666,   /*SerialNumber*/
  1201  			false, /*IsForSale*/
  1202  			0,     /*MinBidAmountNanos*/
  1203  		)
  1204  		require.Error(err)
  1205  		require.Contains(err.Error(), RuleErrorCannotUpdateNonExistentNFT)
  1206  
  1207  		_, _, _, err = _acceptNFTBid(
  1208  			t, chain, db, params, 10,
  1209  			m0Pub,
  1210  			m0Priv,
  1211  			post1Hash,
  1212  			666, /*SerialNumber*/
  1213  			m1Pub,
  1214  			1,  /*BidAmountNanos*/
  1215  			"", /*UnlockableText*/
  1216  		)
  1217  		require.Error(err)
  1218  		require.Contains(err.Error(), RuleErrorNFTBidOnNonExistentNFTEntry)
  1219  	}
  1220  
  1221  	// Error case: can't submit an update txn that doesn't actually update anything.
  1222  	{
  1223  		// <post1, #1> is already for sale.
  1224  		_, _, _, err := _updateNFT(
  1225  			t, chain, db, params, 10,
  1226  			m0Pub,
  1227  			m0Priv,
  1228  			post1Hash,
  1229  			1,    /*SerialNumber*/
  1230  			true, /*IsForSale*/
  1231  			0,    /*MinBidAmountNanos*/
  1232  		)
  1233  		require.Error(err)
  1234  		require.Contains(err.Error(), RuleErrorNFTUpdateMustUpdateIsForSaleStatus)
  1235  	}
  1236  
  1237  	// Finally, accept m2's bid on <post1, #1>.
  1238  	{
  1239  		_acceptNFTBidWithTestMeta(
  1240  			testMeta,
  1241  			10, /*FeeRateNanosPerKB*/
  1242  			m0Pub,
  1243  			m0Priv,
  1244  			post1Hash,
  1245  			1, /*SerialNumber*/
  1246  			m2Pub,
  1247  			2,  /*BidAmountNanos*/
  1248  			"", /*UnlockableText*/
  1249  		)
  1250  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  1251  		require.Equal(0, len(bidEntries))
  1252  	}
  1253  
  1254  	// Update <post1, #2>, so that it is no longer for sale.
  1255  	{
  1256  		_updateNFTWithTestMeta(
  1257  			testMeta,
  1258  			10, /*FeeRateNanosPerKB*/
  1259  			m0Pub,
  1260  			m0Priv,
  1261  			post1Hash,
  1262  			2,     /*SerialNumber*/
  1263  			false, /*IsForSale*/
  1264  			0,     /*MinBidAmountNanos*/
  1265  		)
  1266  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 2)
  1267  		require.Equal(0, len(bidEntries))
  1268  	}
  1269  
  1270  	// Error case: <post1, #1> and <post1, #2> are no longer for sale and should not allow bids.
  1271  	{
  1272  		_, _, _, err := _createNFTBid(
  1273  			t, chain, db, params, 10,
  1274  			m1Pub,
  1275  			m1Priv,
  1276  			post1Hash,
  1277  			1,          /*SerialNumber*/
  1278  			1000000000, /*BidAmountNanos*/
  1279  		)
  1280  		require.Error(err)
  1281  		require.Contains(err.Error(), RuleErrorNFTBidOnNFTThatIsNotForSale)
  1282  
  1283  		_, _, _, err = _createNFTBid(
  1284  			t, chain, db, params, 10,
  1285  			m1Pub,
  1286  			m1Priv,
  1287  			post1Hash,
  1288  			2,          /*SerialNumber*/
  1289  			1000000000, /*BidAmountNanos*/
  1290  		)
  1291  		require.Error(err)
  1292  		require.Contains(err.Error(), RuleErrorNFTBidOnNFTThatIsNotForSale)
  1293  	}
  1294  
  1295  	// Have m1, m2, and m3 bid on <post #2, #1> (which has an unlockable).
  1296  	{
  1297  		bidEntries := DBGetNFTBidEntries(db, post2Hash, 1)
  1298  		require.Equal(0, len(bidEntries))
  1299  
  1300  		_createNFTBidWithTestMeta(
  1301  			testMeta,
  1302  			10, /*FeeRateNanosPerKB*/
  1303  			m1Pub,
  1304  			m1Priv,
  1305  			post2Hash,
  1306  			1, /*SerialNumber*/
  1307  			5, /*BidAmountNanos*/
  1308  		)
  1309  
  1310  		bidEntries = DBGetNFTBidEntries(db, post2Hash, 1)
  1311  		require.Equal(1, len(bidEntries))
  1312  
  1313  		_createNFTBidWithTestMeta(
  1314  			testMeta,
  1315  			10, /*FeeRateNanosPerKB*/
  1316  			m2Pub,
  1317  			m2Priv,
  1318  			post2Hash,
  1319  			1,  /*SerialNumber*/
  1320  			10, /*BidAmountNanos*/
  1321  		)
  1322  
  1323  		bidEntries = DBGetNFTBidEntries(db, post2Hash, 1)
  1324  		require.Equal(2, len(bidEntries))
  1325  
  1326  		// m1 updates their bid to outbid m2.
  1327  		_createNFTBidWithTestMeta(
  1328  			testMeta,
  1329  			10, /*FeeRateNanosPerKB*/
  1330  			m1Pub,
  1331  			m1Priv,
  1332  			post2Hash,
  1333  			1,  /*SerialNumber*/
  1334  			11, /*BidAmountNanos*/
  1335  		)
  1336  
  1337  		// The number of bid entries should not change since this is just an update.
  1338  		bidEntries = DBGetNFTBidEntries(db, post2Hash, 1)
  1339  		require.Equal(2, len(bidEntries))
  1340  
  1341  		_createNFTBidWithTestMeta(
  1342  			testMeta,
  1343  			10, /*FeeRateNanosPerKB*/
  1344  			m3Pub,
  1345  			m3Priv,
  1346  			post2Hash,
  1347  			1,  /*SerialNumber*/
  1348  			12, /*BidAmountNanos*/
  1349  		)
  1350  
  1351  		bidEntries = DBGetNFTBidEntries(db, post2Hash, 1)
  1352  		require.Equal(3, len(bidEntries))
  1353  
  1354  		// m1 updates their bid to outbid m3.
  1355  		_createNFTBidWithTestMeta(
  1356  			testMeta,
  1357  			10, /*FeeRateNanosPerKB*/
  1358  			m1Pub,
  1359  			m1Priv,
  1360  			post2Hash,
  1361  			1,  /*SerialNumber*/
  1362  			13, /*BidAmountNanos*/
  1363  		)
  1364  
  1365  		bidEntries = DBGetNFTBidEntries(db, post2Hash, 1)
  1366  		require.Equal(3, len(bidEntries))
  1367  	}
  1368  
  1369  	// Error case: can't accept a bid for an unlockable NFT, without providing the unlockable.
  1370  	{
  1371  		_, _, _, err = _acceptNFTBid(
  1372  			t, chain, db, params, 10,
  1373  			m0Pub,
  1374  			m0Priv,
  1375  			post2Hash,
  1376  			1, /*SerialNumber*/
  1377  			m3Pub,
  1378  			12, /*BidAmountNanos*/
  1379  			"", /*UnencryptedUnlockableText*/
  1380  		)
  1381  		require.Error(err)
  1382  		require.Contains(err.Error(), RuleErrorUnlockableNFTMustProvideUnlockableText)
  1383  	}
  1384  
  1385  	{
  1386  		unencryptedUnlockableText := "this is an unlockable string"
  1387  
  1388  		// Accepting the bid with an unlockable string should work.
  1389  		_acceptNFTBidWithTestMeta(
  1390  			testMeta,
  1391  			10, /*FeeRateNanosPerKB*/
  1392  			m0Pub,
  1393  			m0Priv,
  1394  			post2Hash,
  1395  			1,                         /*SerialNumber*/
  1396  			m3Pub,                     /*bidderPkBase58Check*/
  1397  			12,                        /*BidAmountNanos*/
  1398  			unencryptedUnlockableText, /*UnencryptedUnlockableText*/
  1399  		)
  1400  
  1401  		// Check and make sure the unlockable looks gucci.
  1402  		nftEntry := DBGetNFTEntryByPostHashSerialNumber(db, post2Hash, 1)
  1403  		require.Equal(nftEntry.IsForSale, false)
  1404  	}
  1405  
  1406  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  1407  	_rollBackTestMetaTxnsAndFlush(testMeta)
  1408  	_applyTestMetaTxnsToMempool(testMeta)
  1409  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  1410  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  1411  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  1412  }
  1413  
  1414  func TestNFTRoyaltiesAndSpendingOfBidderUTXOs(t *testing.T) {
  1415  	assert := assert.New(t)
  1416  	require := require.New(t)
  1417  	_ = assert
  1418  	_ = require
  1419  
  1420  	chain, params, db := NewLowDifficultyBlockchain()
  1421  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  1422  	// Make m3, m4 a paramUpdater for this test
  1423  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  1424  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  1425  
  1426  	// Mine a few blocks to give the senderPkString some money.
  1427  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  1428  	require.NoError(err)
  1429  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  1430  	require.NoError(err)
  1431  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  1432  	require.NoError(err)
  1433  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  1434  	require.NoError(err)
  1435  
  1436  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  1437  	testMeta := &TestMeta{
  1438  		t:           t,
  1439  		chain:       chain,
  1440  		params:      params,
  1441  		db:          db,
  1442  		mempool:     mempool,
  1443  		miner:       miner,
  1444  		savedHeight: chain.blockTip().Height + 1,
  1445  	}
  1446  
  1447  	// Fund all the keys.
  1448  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 100)
  1449  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 1000)
  1450  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 15000)
  1451  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  1452  
  1453  	// Set max copies to a non-zero value to activate NFTs.
  1454  	{
  1455  		_updateGlobalParamsEntryWithTestMeta(
  1456  			testMeta,
  1457  			10, /*FeeRateNanosPerKB*/
  1458  			m4Pub,
  1459  			m4Priv,
  1460  			-1, -1, -1, -1,
  1461  			1000, /*maxCopiesPerNFT*/
  1462  		)
  1463  	}
  1464  
  1465  	// Create a simple post.
  1466  	{
  1467  		_submitPostWithTestMeta(
  1468  			testMeta,
  1469  			10,                                 /*feeRateNanosPerKB*/
  1470  			m0Pub,                              /*updaterPkBase58Check*/
  1471  			m0Priv,                             /*updaterPrivBase58Check*/
  1472  			[]byte{},                           /*postHashToModify*/
  1473  			[]byte{},                           /*parentStakeID*/
  1474  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  1475  			[]byte{},
  1476  			1502947011*1e9, /*tstampNanos*/
  1477  			false /*isHidden*/)
  1478  	}
  1479  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  1480  
  1481  	// Create a profile so me can make an NFT.
  1482  	{
  1483  		_updateProfileWithTestMeta(
  1484  			testMeta,
  1485  			10,            /*feeRateNanosPerKB*/
  1486  			m0Pub,         /*updaterPkBase58Check*/
  1487  			m0Priv,        /*updaterPrivBase58Check*/
  1488  			[]byte{},      /*profilePubKey*/
  1489  			"m2",          /*newUsername*/
  1490  			"i am the m2", /*newDescription*/
  1491  			shortPic,      /*newProfilePic*/
  1492  			10*100,        /*newCreatorBasisPoints*/
  1493  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  1494  			false /*isHidden*/)
  1495  	}
  1496  
  1497  	// Make sure that m0 has coins in circulation so that creator coin royalties can be paid.
  1498  	{
  1499  		_creatorCoinTxnWithTestMeta(
  1500  			testMeta,
  1501  			10,     /*feeRateNanosPerKB*/
  1502  			m0Pub,  /*updaterPkBase58Check*/
  1503  			m0Priv, /*updaterPrivBase58Check*/
  1504  			m0Pub,  /*profilePubKeyBase58Check*/
  1505  			CreatorCoinOperationTypeBuy,
  1506  			29, /*DeSoToSellNanos*/
  1507  			0,  /*CreatorCoinToSellNanos*/
  1508  			0,  /*DeSoToAddNanos*/
  1509  			0,  /*MinDeSoExpectedNanos*/
  1510  			10, /*MinCreatorCoinExpectedNanos*/
  1511  		)
  1512  
  1513  		m0Bal := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
  1514  		require.Equal(uint64(30), m0Bal)
  1515  	}
  1516  	// Initial deso locked before royalties.
  1517  	m0InitialDeSoLocked, _ := _getCreatorCoinInfo(t, db, params, m0Pub)
  1518  	require.Equal(uint64(28), m0InitialDeSoLocked)
  1519  
  1520  	// Error case: m0 should not be able to set >10000 basis points in royalties.
  1521  	{
  1522  		_, _, _, err := _createNFT(
  1523  			t, chain, db, params, 10,
  1524  			m0Pub,
  1525  			m0Priv,
  1526  			post1Hash,
  1527  			10,    /*NumCopies*/
  1528  			false, /*HasUnlockable*/
  1529  			true,  /*IsForSale*/
  1530  			0,     /*MinBidAmountNanos*/
  1531  			0,     /*nftFee*/
  1532  			10000, /*nftRoyaltyToCreatorBasisPoints*/
  1533  			1,     /*nftRoyaltyToCoinBasisPoints*/
  1534  		)
  1535  		require.Error(err)
  1536  		require.Contains(err.Error(), RuleErrorNFTRoyaltyHasTooManyBasisPoints)
  1537  
  1538  		_, _, _, err = _createNFT(
  1539  			t, chain, db, params, 10,
  1540  			m0Pub,
  1541  			m0Priv,
  1542  			post1Hash,
  1543  			10,    /*NumCopies*/
  1544  			false, /*HasUnlockable*/
  1545  			true,  /*IsForSale*/
  1546  			0,     /*MinBidAmountNanos*/
  1547  			0,     /*nftFee*/
  1548  			1,     /*nftRoyaltyToCreatorBasisPoints*/
  1549  			10000, /*nftRoyaltyToCoinBasisPoints*/
  1550  		)
  1551  		require.Error(err)
  1552  		require.Contains(err.Error(), RuleErrorNFTRoyaltyHasTooManyBasisPoints)
  1553  	}
  1554  
  1555  	// Error case: royalty values big enough to overflow should fail.
  1556  	{
  1557  		_, _, _, err := _createNFT(
  1558  			t, chain, db, params, 10,
  1559  			m0Pub,
  1560  			m0Priv,
  1561  			post1Hash,
  1562  			10,               /*NumCopies*/
  1563  			false,            /*HasUnlockable*/
  1564  			true,             /*IsForSale*/
  1565  			0,                /*MinBidAmountNanos*/
  1566  			0,                /*nftFee*/
  1567  			math.MaxUint64-1, /*nftRoyaltyToCreatorBasisPoints*/
  1568  			2,                /*nftRoyaltyToCoinBasisPoints*/
  1569  		)
  1570  		require.Error(err)
  1571  		require.Contains(err.Error(), RuleErrorNFTRoyaltyOverflow)
  1572  	}
  1573  
  1574  	// Create NFT: Let's have m0 create an NFT with 10% royalties for the creator and 20% for the coin.
  1575  	{
  1576  		// Balance before.
  1577  		m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub)
  1578  		require.Equal(uint64(30), m0BalBeforeNFT)
  1579  
  1580  		_createNFTWithTestMeta(
  1581  			testMeta,
  1582  			10, /*FeeRateNanosPerKB*/
  1583  			m0Pub,
  1584  			m0Priv,
  1585  			post1Hash,
  1586  			10,    /*NumCopies*/
  1587  			false, /*HasUnlockable*/
  1588  			true,  /*IsForSale*/
  1589  			0,     /*MinBidAmountNanos*/
  1590  			0,     /*nftFee*/
  1591  			1000,  /*nftRoyaltyToCreatorBasisPoints*/
  1592  			2000,  /*nftRoyaltyToCoinBasisPoints*/
  1593  		)
  1594  
  1595  		// Balance after. Since the default NFT fee is 0, m0 is only charged the nanos per kb fee.
  1596  		m0BalAfterNFT := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
  1597  		require.Equal(uint64(29), m0BalAfterNFT)
  1598  	}
  1599  
  1600  	// 1 nano bid: Have m1 make a bid on <post1, #1>, accept it and check the royalties.
  1601  	{
  1602  		bidAmountNanos := uint64(1)
  1603  		serialNumber := uint64(1)
  1604  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  1605  		require.Equal(0, len(bidEntries))
  1606  
  1607  		_createNFTBidWithTestMeta(
  1608  			testMeta,
  1609  			10, /*FeeRateNanosPerKB*/
  1610  			m1Pub,
  1611  			m1Priv,
  1612  			post1Hash,
  1613  			serialNumber,   /*SerialNumber*/
  1614  			bidAmountNanos, /*BidAmountNanos*/
  1615  		)
  1616  
  1617  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  1618  		require.Equal(1, len(bidEntries))
  1619  
  1620  		// Owner balance before.
  1621  		m0BalBefore := _getBalance(t, chain, nil, m0Pub)
  1622  		require.Equal(uint64(29), m0BalBefore)
  1623  
  1624  		// Bidder balance before.
  1625  		m1BalBefore := _getBalance(t, chain, nil, m1Pub)
  1626  		require.Equal(uint64(999), m1BalBefore)
  1627  
  1628  		_acceptNFTBidWithTestMeta(
  1629  			testMeta,
  1630  			10, /*FeeRateNanosPerKB*/
  1631  			m0Pub,
  1632  			m0Priv,
  1633  			post1Hash,
  1634  			serialNumber, /*SerialNumber*/
  1635  			m1Pub,        /*bidderPkBase58Check*/
  1636  			bidAmountNanos,
  1637  			"", /*UnencryptedUnlockableText*/
  1638  		)
  1639  
  1640  		// Check royalties. 10% for the creator, 10% for the coin.
  1641  		m0BalAfter := _getBalance(t, chain, nil, m0Pub)
  1642  		// In order to prevent money printing, <1 nano royalties are rounded down to zero.
  1643  		expectedCreatorRoyalty := bidAmountNanos / 10
  1644  		require.Equal(uint64(0), expectedCreatorRoyalty)
  1645  		expectedCoinRoyalty := bidAmountNanos / 10
  1646  		require.Equal(uint64(0), expectedCoinRoyalty)
  1647  		bidAmountMinusRoyalties := bidAmountNanos - expectedCoinRoyalty - expectedCreatorRoyalty
  1648  		require.Equal(m0BalBefore-2+bidAmountMinusRoyalties+expectedCreatorRoyalty, m0BalAfter)
  1649  		require.Equal(uint64(28), m0BalAfter)
  1650  		// Make sure that the bidder's balance decreased by the bid amount.
  1651  		m1BalAfter := _getBalance(t, chain, nil, m1Pub)
  1652  		require.Equal(m1BalBefore-bidAmountNanos, m1BalAfter)
  1653  		require.Equal(uint64(998), m1BalAfter)
  1654  		// Creator coin: zero royalties should be paid.
  1655  		desoLocked, _ := _getCreatorCoinInfo(t, db, params, m0Pub)
  1656  		require.Equal(m0InitialDeSoLocked+expectedCoinRoyalty, desoLocked)
  1657  	}
  1658  
  1659  	// 10 nano bid: Have m1 make a bid on <post1, #2>, accept it and check the royalties.
  1660  	{
  1661  		bidAmountNanos := uint64(10)
  1662  		serialNumber := uint64(2)
  1663  		bidEntries := DBGetNFTBidEntries(db, post1Hash, serialNumber)
  1664  		require.Equal(0, len(bidEntries))
  1665  
  1666  		_createNFTBidWithTestMeta(
  1667  			testMeta,
  1668  			10, /*FeeRateNanosPerKB*/
  1669  			m1Pub,
  1670  			m1Priv,
  1671  			post1Hash,
  1672  			serialNumber,   /*SerialNumber*/
  1673  			bidAmountNanos, /*BidAmountNanos*/
  1674  		)
  1675  
  1676  		bidEntries = DBGetNFTBidEntries(db, post1Hash, serialNumber)
  1677  		require.Equal(1, len(bidEntries))
  1678  
  1679  		// Balance before.
  1680  		m0BalBefore := _getBalance(t, chain, nil, m0Pub)
  1681  		require.Equal(uint64(28), m0BalBefore)
  1682  
  1683  		// Bidder balance before.
  1684  		m1BalBefore := _getBalance(t, chain, nil, m1Pub)
  1685  		require.Equal(uint64(997), m1BalBefore)
  1686  
  1687  		_acceptNFTBidWithTestMeta(
  1688  			testMeta,
  1689  			10, /*FeeRateNanosPerKB*/
  1690  			m0Pub,
  1691  			m0Priv,
  1692  			post1Hash,
  1693  			serialNumber, /*SerialNumber*/
  1694  			m1Pub,        /*bidderPkBase58Check*/
  1695  			bidAmountNanos,
  1696  			"", /*UnencryptedUnlockableText*/
  1697  		)
  1698  
  1699  		// Check royalties. 10% for the creator, 20% for the coin.
  1700  		m0BalAfter := _getBalance(t, chain, nil, m0Pub)
  1701  		expectedCreatorRoyalty := bidAmountNanos / 10
  1702  		require.Equal(uint64(1), expectedCreatorRoyalty)
  1703  		expectedCoinRoyalty := 2 * bidAmountNanos / 10
  1704  		require.Equal(uint64(2), expectedCoinRoyalty)
  1705  		bidAmountMinusRoyalties := bidAmountNanos - expectedCoinRoyalty - expectedCreatorRoyalty
  1706  		require.Equal(m0BalBefore-2+bidAmountMinusRoyalties+expectedCreatorRoyalty, m0BalAfter)
  1707  		require.Equal(uint64(34), m0BalAfter)
  1708  		// Make sure that the bidder's balance decreased by the bid amount.
  1709  		m1BalAfter := _getBalance(t, chain, nil, m1Pub)
  1710  		require.Equal(m1BalBefore-bidAmountNanos, m1BalAfter)
  1711  		require.Equal(uint64(987), m1BalAfter)
  1712  		// Creator coin.
  1713  		desoLocked, _ := _getCreatorCoinInfo(t, db, params, m0Pub)
  1714  		require.Equal(m0InitialDeSoLocked+expectedCoinRoyalty, desoLocked)
  1715  	}
  1716  
  1717  	// 100 nano bid: Have m1 make a bid on <post1, #3>, accept it and check the royalties.
  1718  	{
  1719  		m0DeSoLocked, _ := _getCreatorCoinInfo(t, db, params, m0Pub)
  1720  		require.Equal(uint64(30), m0DeSoLocked)
  1721  
  1722  		bidAmountNanos := uint64(100)
  1723  		serialNumber := uint64(3)
  1724  		bidEntries := DBGetNFTBidEntries(db, post1Hash, serialNumber)
  1725  		require.Equal(0, len(bidEntries))
  1726  
  1727  		_createNFTBidWithTestMeta(
  1728  			testMeta,
  1729  			10, /*FeeRateNanosPerKB*/
  1730  			m1Pub,
  1731  			m1Priv,
  1732  			post1Hash,
  1733  			serialNumber,   /*SerialNumber*/
  1734  			bidAmountNanos, /*BidAmountNanos*/
  1735  		)
  1736  
  1737  		bidEntries = DBGetNFTBidEntries(db, post1Hash, serialNumber)
  1738  		require.Equal(1, len(bidEntries))
  1739  
  1740  		// Balance before.
  1741  		m0BalBefore := _getBalance(t, chain, nil, m0Pub)
  1742  		require.Equal(uint64(34), m0BalBefore)
  1743  
  1744  		// Bidder balance before.
  1745  		m1BalBefore := _getBalance(t, chain, nil, m1Pub)
  1746  		require.Equal(uint64(986), m1BalBefore)
  1747  
  1748  		_acceptNFTBidWithTestMeta(
  1749  			testMeta,
  1750  			10, /*FeeRateNanosPerKB*/
  1751  			m0Pub,
  1752  			m0Priv,
  1753  			post1Hash,
  1754  			serialNumber, /*SerialNumber*/
  1755  			m1Pub,        /*bidderPkBase58Check*/
  1756  			bidAmountNanos,
  1757  			"", /*UnencryptedUnlockableText*/
  1758  		)
  1759  
  1760  		// Check royalties. 10% for the creator, 20% for the coin.
  1761  		m0BalAfter := _getBalance(t, chain, nil, m0Pub)
  1762  		expectedCreatorRoyalty := bidAmountNanos / 10
  1763  		require.Equal(uint64(10), expectedCreatorRoyalty)
  1764  		expectedCoinRoyalty := 2 * bidAmountNanos / 10
  1765  		require.Equal(uint64(20), expectedCoinRoyalty)
  1766  		bidAmountMinusRoyalties := bidAmountNanos - expectedCoinRoyalty - expectedCreatorRoyalty
  1767  		require.Equal(m0BalBefore-2+bidAmountMinusRoyalties+expectedCreatorRoyalty, m0BalAfter)
  1768  		require.Equal(uint64(112), m0BalAfter)
  1769  		// Make sure that the bidder's balance decreased by the bid amount.
  1770  		m1BalAfter := _getBalance(t, chain, nil, m1Pub)
  1771  		require.Equal(m1BalBefore-bidAmountNanos, m1BalAfter)
  1772  		require.Equal(uint64(886), m1BalAfter)
  1773  		// Creator coin.
  1774  		desoLocked, _ := _getCreatorCoinInfo(t, db, params, m0Pub)
  1775  		require.Equal(m0DeSoLocked+expectedCoinRoyalty, desoLocked)
  1776  	}
  1777  
  1778  	// Put <post1, #1> up for sale again and make sure royalties still work.
  1779  	{
  1780  		_updateNFTWithTestMeta(
  1781  			testMeta,
  1782  			10, /*FeeRateNanosPerKB*/
  1783  			m1Pub,
  1784  			m1Priv,
  1785  			post1Hash,
  1786  			1,    /*SerialNumber*/
  1787  			true, /*IsForSale*/
  1788  			0,    /*MinBidAmountNanos*/
  1789  		)
  1790  	}
  1791  
  1792  	// 10000 nano bid: Have m3 make a bid on <post1, #1>, accept it and check the royalties.
  1793  	{
  1794  		m0DeSoLocked, _ := _getCreatorCoinInfo(t, db, params, m0Pub)
  1795  		require.Equal(uint64(50), m0DeSoLocked)
  1796  
  1797  		bidAmountNanos := uint64(10000)
  1798  		serialNumber := uint64(1)
  1799  		bidEntries := DBGetNFTBidEntries(db, post1Hash, serialNumber)
  1800  		require.Equal(0, len(bidEntries))
  1801  
  1802  		_createNFTBidWithTestMeta(
  1803  			testMeta,
  1804  			10, /*FeeRateNanosPerKB*/
  1805  			m3Pub,
  1806  			m3Priv,
  1807  			post1Hash,
  1808  			serialNumber,   /*SerialNumber*/
  1809  			bidAmountNanos, /*BidAmountNanos*/
  1810  		)
  1811  
  1812  		bidEntries = DBGetNFTBidEntries(db, post1Hash, serialNumber)
  1813  		require.Equal(1, len(bidEntries))
  1814  
  1815  		// Balance before.
  1816  		m0BalBefore := _getBalance(t, chain, nil, m0Pub)
  1817  		require.Equal(uint64(112), m0BalBefore)
  1818  		m1BalBefore := _getBalance(t, chain, nil, m1Pub)
  1819  		require.Equal(uint64(885), m1BalBefore)
  1820  		m3BalBefore := _getBalance(t, chain, nil, m3Pub)
  1821  		require.Equal(uint64(14999), m3BalBefore)
  1822  
  1823  		_acceptNFTBidWithTestMeta(
  1824  			testMeta,
  1825  			10, /*FeeRateNanosPerKB*/
  1826  			m1Pub,
  1827  			m1Priv,
  1828  			post1Hash,
  1829  			serialNumber, /*SerialNumber*/
  1830  			m3Pub,        /*bidderPkBase58Check*/
  1831  			bidAmountNanos,
  1832  			"", /*UnencryptedUnlockableText*/
  1833  		)
  1834  
  1835  		// Check royalties. 10% for the creator, 10% for the coin.
  1836  		expectedCreatorRoyalty := bidAmountNanos / 10
  1837  		require.Equal(uint64(1000), expectedCreatorRoyalty)
  1838  		expectedCoinRoyalty := 2 * bidAmountNanos / 10
  1839  		require.Equal(uint64(2000), expectedCoinRoyalty)
  1840  		bidAmountMinusRoyalties := bidAmountNanos - expectedCoinRoyalty - expectedCreatorRoyalty
  1841  
  1842  		m0BalAfter := _getBalance(t, chain, nil, m0Pub)
  1843  		require.Equal(m0BalBefore+expectedCreatorRoyalty, m0BalAfter)
  1844  		require.Equal(uint64(1112), m0BalAfter)
  1845  
  1846  		m1BalAfter := _getBalance(t, chain, nil, m1Pub)
  1847  		require.Equal(m1BalBefore-2+bidAmountMinusRoyalties, m1BalAfter)
  1848  		require.Equal(uint64(7883), m1BalAfter)
  1849  
  1850  		// Make sure m3's balance was decreased appropriately.
  1851  		m3BalAfter := _getBalance(t, chain, nil, m3Pub)
  1852  		require.Equal(m3BalBefore-bidAmountNanos, m3BalAfter)
  1853  		require.Equal(uint64(4999), m3BalAfter)
  1854  
  1855  		// Creator coin.
  1856  		desoLocked, _ := _getCreatorCoinInfo(t, db, params, m0Pub)
  1857  		require.Equal(m0DeSoLocked+expectedCoinRoyalty, desoLocked)
  1858  	}
  1859  
  1860  	// Error case: Let's make sure that no royalties are paid if there are no coins in circulation.
  1861  	{
  1862  		_, coinsInCirculationNanos := _getCreatorCoinInfo(t, db, params, m0Pub)
  1863  		require.Equal(uint64(30365901), coinsInCirculationNanos)
  1864  
  1865  		// Sell all the coins.
  1866  		_creatorCoinTxnWithTestMeta(
  1867  			testMeta,
  1868  			10,     /*feeRateNanosPerKB*/
  1869  			m0Pub,  /*updaterPkBase58Check*/
  1870  			m0Priv, /*updaterPrivBase58Check*/
  1871  			m0Pub,  /*profilePubKeyBase58Check*/
  1872  			CreatorCoinOperationTypeSell,
  1873  			0,                       /*DeSoToSellNanos*/
  1874  			coinsInCirculationNanos, /*CreatorCoinToSellNanos*/
  1875  			0,                       /*DeSoToAddNanos*/
  1876  			0,                       /*MinDeSoExpectedNanos*/
  1877  			0,                       /*MinCreatorCoinExpectedNanos*/
  1878  		)
  1879  
  1880  		// Create a bid on <post1, #9>, which is still for sale.
  1881  		bidAmountNanos := uint64(100)
  1882  		serialNumber := uint64(9)
  1883  		bidEntries := DBGetNFTBidEntries(db, post1Hash, serialNumber)
  1884  		require.Equal(0, len(bidEntries))
  1885  
  1886  		_createNFTBidWithTestMeta(
  1887  			testMeta,
  1888  			10, /*FeeRateNanosPerKB*/
  1889  			m3Pub,
  1890  			m3Priv,
  1891  			post1Hash,
  1892  			serialNumber,   /*SerialNumber*/
  1893  			bidAmountNanos, /*BidAmountNanos*/
  1894  		)
  1895  
  1896  		bidEntries = DBGetNFTBidEntries(db, post1Hash, serialNumber)
  1897  		require.Equal(1, len(bidEntries))
  1898  
  1899  		// Balance before.
  1900  		m0BalBefore := _getBalance(t, chain, nil, m0Pub)
  1901  		require.Equal(uint64(3160), m0BalBefore)
  1902  
  1903  		_acceptNFTBidWithTestMeta(
  1904  			testMeta,
  1905  			10, /*FeeRateNanosPerKB*/
  1906  			m0Pub,
  1907  			m0Priv,
  1908  			post1Hash,
  1909  			serialNumber, /*SerialNumber*/
  1910  			m3Pub,        /*bidderPkBase58Check*/
  1911  			bidAmountNanos,
  1912  			"", /*UnencryptedUnlockableText*/
  1913  		)
  1914  
  1915  		// Check royalties. 10% for the creator, 20% for the coin.
  1916  		expectedCreatorRoyalty := bidAmountNanos / 10
  1917  		require.Equal(uint64(10), expectedCreatorRoyalty)
  1918  		expectedCoinRoyalty := 2 * bidAmountNanos / 10
  1919  		require.Equal(uint64(20), expectedCoinRoyalty)
  1920  		bidAmountMinusRoyalties := bidAmountNanos - expectedCoinRoyalty - expectedCreatorRoyalty
  1921  
  1922  		m0BalAfter := _getBalance(t, chain, nil, m0Pub)
  1923  		require.Equal(m0BalBefore-2+bidAmountMinusRoyalties+expectedCreatorRoyalty, m0BalAfter)
  1924  		require.Equal(uint64(3238), m0BalAfter)
  1925  
  1926  		// Creator coin --> Make sure no royalties were added.
  1927  		desoLocked, _ := _getCreatorCoinInfo(t, db, params, m0Pub)
  1928  		require.Equal(uint64(0), desoLocked)
  1929  	}
  1930  
  1931  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  1932  	_rollBackTestMetaTxnsAndFlush(testMeta)
  1933  	_applyTestMetaTxnsToMempool(testMeta)
  1934  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  1935  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  1936  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  1937  }
  1938  
  1939  func TestNFTSerialNumberZeroBid(t *testing.T) {
  1940  	assert := assert.New(t)
  1941  	require := require.New(t)
  1942  	_ = assert
  1943  	_ = require
  1944  
  1945  	chain, params, db := NewLowDifficultyBlockchain()
  1946  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  1947  	// Make m3, m4 a paramUpdater for this test
  1948  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  1949  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  1950  
  1951  	// Mine a few blocks to give the senderPkString some money.
  1952  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  1953  	require.NoError(err)
  1954  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  1955  	require.NoError(err)
  1956  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  1957  	require.NoError(err)
  1958  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  1959  	require.NoError(err)
  1960  
  1961  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  1962  	testMeta := &TestMeta{
  1963  		t:           t,
  1964  		chain:       chain,
  1965  		params:      params,
  1966  		db:          db,
  1967  		mempool:     mempool,
  1968  		miner:       miner,
  1969  		savedHeight: chain.blockTip().Height + 1,
  1970  	}
  1971  
  1972  	// Fund all the keys.
  1973  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 100)
  1974  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 15000)
  1975  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 15000)
  1976  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 15000)
  1977  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  1978  
  1979  	// Set max copies to a non-zero value to activate NFTs.
  1980  	{
  1981  		_updateGlobalParamsEntryWithTestMeta(
  1982  			testMeta,
  1983  			10, /*FeeRateNanosPerKB*/
  1984  			m4Pub,
  1985  			m4Priv,
  1986  			-1, -1, -1, -1,
  1987  			1000, /*maxCopiesPerNFT*/
  1988  		)
  1989  	}
  1990  
  1991  	// Create two posts for testing.
  1992  	{
  1993  		_submitPostWithTestMeta(
  1994  			testMeta,
  1995  			10,                                 /*feeRateNanosPerKB*/
  1996  			m0Pub,                              /*updaterPkBase58Check*/
  1997  			m0Priv,                             /*updaterPrivBase58Check*/
  1998  			[]byte{},                           /*postHashToModify*/
  1999  			[]byte{},                           /*parentStakeID*/
  2000  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  2001  			[]byte{},
  2002  			1502947011*1e9, /*tstampNanos*/
  2003  			false /*isHidden*/)
  2004  
  2005  		_submitPostWithTestMeta(
  2006  			testMeta,
  2007  			10,                                 /*feeRateNanosPerKB*/
  2008  			m0Pub,                              /*updaterPkBase58Check*/
  2009  			m0Priv,                             /*updaterPrivBase58Check*/
  2010  			[]byte{},                           /*postHashToModify*/
  2011  			[]byte{},                           /*parentStakeID*/
  2012  			&DeSoBodySchema{Body: "m0 post 2"}, /*body*/
  2013  			[]byte{},
  2014  			1502947011*1e9, /*tstampNanos*/
  2015  			false /*isHidden*/)
  2016  	}
  2017  	post1Hash := testMeta.txns[len(testMeta.txns)-2].Hash()
  2018  	post2Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  2019  
  2020  	// Create a profile so me can make an NFT.
  2021  	{
  2022  		_updateProfileWithTestMeta(
  2023  			testMeta,
  2024  			10,            /*feeRateNanosPerKB*/
  2025  			m0Pub,         /*updaterPkBase58Check*/
  2026  			m0Priv,        /*updaterPrivBase58Check*/
  2027  			[]byte{},      /*profilePubKey*/
  2028  			"m2",          /*newUsername*/
  2029  			"i am the m2", /*newDescription*/
  2030  			shortPic,      /*newProfilePic*/
  2031  			10*100,        /*newCreatorBasisPoints*/
  2032  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  2033  			false /*isHidden*/)
  2034  	}
  2035  
  2036  	// Create NFT: Let's have m0 create two NFTs for testing.
  2037  	{
  2038  		// Balance before.
  2039  		m0BalBeforeNFTs := _getBalance(t, chain, nil, m0Pub)
  2040  		require.Equal(uint64(59), m0BalBeforeNFTs)
  2041  
  2042  		// Create an NFT with a ton of copies for testing accepting bids.
  2043  		_createNFTWithTestMeta(
  2044  			testMeta,
  2045  			10, /*FeeRateNanosPerKB*/
  2046  			m0Pub,
  2047  			m0Priv,
  2048  			post1Hash,
  2049  			100,   /*NumCopies*/
  2050  			false, /*HasUnlockable*/
  2051  			true,  /*IsForSale*/
  2052  			0,     /*MinBidAmountNanos*/
  2053  			0,     /*nftFee*/
  2054  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  2055  			0,     /*nftRoyaltyToCoinBasisPoints*/
  2056  		)
  2057  
  2058  		// Create an NFT with one copy to test making a standing offer on an NFT that isn't for sale.
  2059  		_createNFTWithTestMeta(
  2060  			testMeta,
  2061  			10, /*FeeRateNanosPerKB*/
  2062  			m0Pub,
  2063  			m0Priv,
  2064  			post2Hash,
  2065  			1,     /*NumCopies*/
  2066  			false, /*HasUnlockable*/
  2067  			false, /*IsForSale*/
  2068  			0,     /*MinBidAmountNanos*/
  2069  			0,     /*nftFee*/
  2070  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  2071  			0,     /*nftRoyaltyToCoinBasisPoints*/
  2072  		)
  2073  
  2074  		// Balance after. Since the default NFT fee is 0, m0 is only charged the nanos per kb fee.
  2075  		m0BalAfterNFTs := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
  2076  		require.Equal(m0BalBeforeNFTs-uint64(2), m0BalAfterNFTs)
  2077  	}
  2078  
  2079  	// <Post2, #1> (the only copy of this NFT) is not for sale.  Ensure that we can make a #0 bid.
  2080  	{
  2081  		bidEntries := DBGetNFTBidEntries(db, post2Hash, 0)
  2082  		require.Equal(0, len(bidEntries))
  2083  
  2084  		// m1: This is a standing offer for the post 2 NFT that can be accepted at any time.
  2085  		_createNFTBidWithTestMeta(
  2086  			testMeta,
  2087  			10, /*FeeRateNanosPerKB*/
  2088  			m1Pub,
  2089  			m1Priv,
  2090  			post2Hash,
  2091  			0,   /*SerialNumber*/
  2092  			100, /*BidAmountNanos*/
  2093  		)
  2094  
  2095  		bidEntries = DBGetNFTBidEntries(db, post2Hash, 0)
  2096  		require.Equal(1, len(bidEntries))
  2097  	}
  2098  
  2099  	// Have m1,m2,m3 make some bids, including a bid on serial #0.
  2100  	{
  2101  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  2102  		require.Equal(0, len(bidEntries))
  2103  
  2104  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 0)
  2105  		require.Equal(0, len(bidEntries))
  2106  
  2107  		// m1: This is a blanket bid on any serial number of post1.
  2108  		_createNFTBidWithTestMeta(
  2109  			testMeta,
  2110  			10, /*FeeRateNanosPerKB*/
  2111  			m1Pub,
  2112  			m1Priv,
  2113  			post1Hash,
  2114  			0,   /*SerialNumber*/
  2115  			100, /*BidAmountNanos*/
  2116  		)
  2117  
  2118  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 0)
  2119  		require.Equal(1, len(bidEntries))
  2120  
  2121  		// m1: This is a specific bid for serial #1 of post1.
  2122  		_createNFTBidWithTestMeta(
  2123  			testMeta,
  2124  			10, /*FeeRateNanosPerKB*/
  2125  			m1Pub,
  2126  			m1Priv,
  2127  			post1Hash,
  2128  			1,    /*SerialNumber*/
  2129  			1000, /*BidAmountNanos*/
  2130  		)
  2131  
  2132  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  2133  		require.Equal(1, len(bidEntries))
  2134  
  2135  		// m2: Add a bid from m2 for fun.
  2136  		_createNFTBidWithTestMeta(
  2137  			testMeta,
  2138  			10, /*FeeRateNanosPerKB*/
  2139  			m2Pub,
  2140  			m2Priv,
  2141  			post1Hash,
  2142  			1,   /*SerialNumber*/
  2143  			999, /*BidAmountNanos*/
  2144  		)
  2145  
  2146  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  2147  		require.Equal(2, len(bidEntries))
  2148  
  2149  		// m3: Add a blanket bid from m3 for fun.
  2150  		_createNFTBidWithTestMeta(
  2151  			testMeta,
  2152  			10, /*FeeRateNanosPerKB*/
  2153  			m3Pub,
  2154  			m3Priv,
  2155  			post1Hash,
  2156  			0,   /*SerialNumber*/
  2157  			999, /*BidAmountNanos*/
  2158  		)
  2159  
  2160  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 0)
  2161  		require.Equal(2, len(bidEntries))
  2162  	}
  2163  
  2164  	// Error case: m1 has two active bids. One for serial #1 for 1000 nanos, and one for
  2165  	// serial #0 for 100 nanos. m0 can accept the serial #0 bid on any serial number. In this
  2166  	// case they try and accept it for serial #2 while spoofing the 1000 nano bid amount from
  2167  	// the serial #1 bid.  This should obviously fail.
  2168  	//
  2169  	// In addition, m0 should not be able to accept the serial #0 bid on serial #1 since it is
  2170  	// trumped by the specific serial #1 bid placed by m1.
  2171  	{
  2172  		_, _, _, err = _acceptNFTBid(
  2173  			t, chain, db, params, 10,
  2174  			m0Pub,
  2175  			m0Priv,
  2176  			post1Hash,
  2177  			2, /*SerialNumber*/
  2178  			m1Pub,
  2179  			1000, /*BidAmountNanos*/
  2180  			"",   /*UnlockableText*/
  2181  		)
  2182  		require.Error(err)
  2183  		require.Contains(err.Error(), RuleErrorAcceptedNFTBidAmountDoesNotMatch)
  2184  
  2185  		_, _, _, err = _acceptNFTBid(
  2186  			t, chain, db, params, 10,
  2187  			m0Pub,
  2188  			m0Priv,
  2189  			post1Hash,
  2190  			1, /*SerialNumber*/
  2191  			m1Pub,
  2192  			100, /*BidAmountNanos*/
  2193  			"",  /*UnlockableText*/
  2194  		)
  2195  		require.Error(err)
  2196  		require.Contains(err.Error(), RuleErrorAcceptedNFTBidAmountDoesNotMatch)
  2197  	}
  2198  
  2199  	// Accept some bids!
  2200  	{
  2201  		// Balance before.
  2202  		m0BalBefore := _getBalance(t, chain, nil, m0Pub)
  2203  		require.Equal(uint64(57), m0BalBefore)
  2204  
  2205  		// This will accept m1's serial #0 bid.
  2206  		_acceptNFTBidWithTestMeta(
  2207  			testMeta,
  2208  			10, /*FeeRateNanosPerKB*/
  2209  			m0Pub,
  2210  			m0Priv,
  2211  			post1Hash,
  2212  			2,     /*SerialNumber*/
  2213  			m1Pub, /*bidderPkBase58Check*/
  2214  			100,   /*bidAmountNanos*/
  2215  			"",    /*UnencryptedUnlockableText*/
  2216  		)
  2217  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 0)
  2218  		require.Equal(1, len(bidEntries))
  2219  
  2220  		_acceptNFTBidWithTestMeta(
  2221  			testMeta,
  2222  			10, /*FeeRateNanosPerKB*/
  2223  			m0Pub,
  2224  			m0Priv,
  2225  			post1Hash,
  2226  			1,     /*SerialNumber*/
  2227  			m1Pub, /*bidderPkBase58Check*/
  2228  			1000,  /*bidAmountNanos*/
  2229  			"",    /*UnencryptedUnlockableText*/
  2230  		)
  2231  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  2232  		require.Equal(0, len(bidEntries))
  2233  
  2234  		// This will accept m3's serial #0 bid.
  2235  		_acceptNFTBidWithTestMeta(
  2236  			testMeta,
  2237  			10, /*FeeRateNanosPerKB*/
  2238  			m0Pub,
  2239  			m0Priv,
  2240  			post1Hash,
  2241  			3,     /*SerialNumber*/
  2242  			m3Pub, /*bidderPkBase58Check*/
  2243  			999,   /*bidAmountNanos*/
  2244  			"",    /*UnencryptedUnlockableText*/
  2245  		)
  2246  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 0)
  2247  		require.Equal(0, len(bidEntries))
  2248  
  2249  		// This NFT doesn't have royalties so m0's balance should be directly related to the bids accepted.
  2250  		m0BalAfter := _getBalance(t, chain, nil, m0Pub)
  2251  		require.Equal(m0BalBefore-6+100+1000+999, m0BalAfter)
  2252  		require.Equal(uint64(2150), m0BalAfter)
  2253  	}
  2254  
  2255  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  2256  	_rollBackTestMetaTxnsAndFlush(testMeta)
  2257  	_applyTestMetaTxnsToMempool(testMeta)
  2258  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  2259  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  2260  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  2261  }
  2262  
  2263  func TestNFTMinimumBidAmount(t *testing.T) {
  2264  	assert := assert.New(t)
  2265  	require := require.New(t)
  2266  	_ = assert
  2267  	_ = require
  2268  
  2269  	chain, params, db := NewLowDifficultyBlockchain()
  2270  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  2271  	// Make m3, m4 a paramUpdater for this test
  2272  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  2273  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  2274  
  2275  	// Mine a few blocks to give the senderPkString some money.
  2276  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2277  	require.NoError(err)
  2278  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2279  	require.NoError(err)
  2280  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2281  	require.NoError(err)
  2282  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2283  	require.NoError(err)
  2284  
  2285  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  2286  	testMeta := &TestMeta{
  2287  		t:           t,
  2288  		chain:       chain,
  2289  		params:      params,
  2290  		db:          db,
  2291  		mempool:     mempool,
  2292  		miner:       miner,
  2293  		savedHeight: chain.blockTip().Height + 1,
  2294  	}
  2295  
  2296  	// Fund all the keys.
  2297  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 15000)
  2298  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 15000)
  2299  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 15000)
  2300  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 15000)
  2301  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  2302  
  2303  	// Set max copies to a non-zero value to activate NFTs.
  2304  	{
  2305  		_updateGlobalParamsEntryWithTestMeta(
  2306  			testMeta,
  2307  			10, /*FeeRateNanosPerKB*/
  2308  			m4Pub,
  2309  			m4Priv,
  2310  			-1, -1, -1, -1,
  2311  			1000, /*maxCopiesPerNFT*/
  2312  		)
  2313  	}
  2314  
  2315  	// Create a simple post.
  2316  	{
  2317  		_submitPostWithTestMeta(
  2318  			testMeta,
  2319  			10,                                 /*feeRateNanosPerKB*/
  2320  			m0Pub,                              /*updaterPkBase58Check*/
  2321  			m0Priv,                             /*updaterPrivBase58Check*/
  2322  			[]byte{},                           /*postHashToModify*/
  2323  			[]byte{},                           /*parentStakeID*/
  2324  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  2325  			[]byte{},
  2326  			1502947011*1e9, /*tstampNanos*/
  2327  			false /*isHidden*/)
  2328  	}
  2329  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  2330  
  2331  	// Create a profile so me can make an NFT.
  2332  	{
  2333  		_updateProfileWithTestMeta(
  2334  			testMeta,
  2335  			10,            /*feeRateNanosPerKB*/
  2336  			m0Pub,         /*updaterPkBase58Check*/
  2337  			m0Priv,        /*updaterPrivBase58Check*/
  2338  			[]byte{},      /*profilePubKey*/
  2339  			"m2",          /*newUsername*/
  2340  			"i am the m2", /*newDescription*/
  2341  			shortPic,      /*newProfilePic*/
  2342  			10*100,        /*newCreatorBasisPoints*/
  2343  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  2344  			false /*isHidden*/)
  2345  	}
  2346  
  2347  	// Create NFT with a minimum bid amount.
  2348  	{
  2349  		// Balance before.
  2350  		m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub)
  2351  		require.Equal(uint64(14960), m0BalBeforeNFT)
  2352  
  2353  		_createNFTWithTestMeta(
  2354  			testMeta,
  2355  			10, /*FeeRateNanosPerKB*/
  2356  			m0Pub,
  2357  			m0Priv,
  2358  			post1Hash,
  2359  			100,   /*NumCopies*/
  2360  			false, /*HasUnlockable*/
  2361  			true,  /*IsForSale*/
  2362  			1111,  /*MinBidAmountNanos*/
  2363  			0,     /*nftFee*/
  2364  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  2365  			0,     /*nftRoyaltyToCoinBasisPoints*/
  2366  		)
  2367  
  2368  		// Balance after. Since the default NFT fee is 0, m0 is only charged the nanos per kb fee.
  2369  		m0BalAfterNFT := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
  2370  		require.Equal(uint64(14959), m0BalAfterNFT)
  2371  	}
  2372  
  2373  	// Error case: Attempt to make some bids below the minimum bid amount, they should error.
  2374  	{
  2375  		_, _, _, err := _createNFTBid(
  2376  			t, chain, db, params, 10,
  2377  			m1Pub,
  2378  			m1Priv,
  2379  			post1Hash,
  2380  			1, /*SerialNumber*/
  2381  			0, /*BidAmountNanos*/
  2382  		)
  2383  		require.Error(err)
  2384  		require.Contains(err.Error(), RuleErrorNFTBidLessThanMinBidAmountNanos)
  2385  
  2386  		_, _, _, err = _createNFTBid(
  2387  			t, chain, db, params, 10,
  2388  			m1Pub,
  2389  			m1Priv,
  2390  			post1Hash,
  2391  			1,    /*SerialNumber*/
  2392  			1110, /*BidAmountNanos*/
  2393  		)
  2394  		require.Error(err)
  2395  		require.Contains(err.Error(), RuleErrorNFTBidLessThanMinBidAmountNanos)
  2396  	}
  2397  
  2398  	// Have m1,m2,m3 make some legitimate bids, including a bid on serial #0.
  2399  	{
  2400  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  2401  		require.Equal(0, len(bidEntries))
  2402  
  2403  		// m1 --> <post1, #1>
  2404  		_createNFTBidWithTestMeta(
  2405  			testMeta,
  2406  			10, /*FeeRateNanosPerKB*/
  2407  			m1Pub,
  2408  			m1Priv,
  2409  			post1Hash,
  2410  			1,    /*SerialNumber*/
  2411  			1111, /*BidAmountNanos*/
  2412  		)
  2413  
  2414  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  2415  		require.Equal(1, len(bidEntries))
  2416  
  2417  		// m1 --> <post1, #0> (This bid can be any amount since it is a blanket bid)
  2418  		_createNFTBidWithTestMeta(
  2419  			testMeta,
  2420  			10, /*FeeRateNanosPerKB*/
  2421  			m1Pub,
  2422  			m1Priv,
  2423  			post1Hash,
  2424  			0,  /*SerialNumber*/
  2425  			10, /*BidAmountNanos*/
  2426  		)
  2427  
  2428  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 0)
  2429  		require.Equal(1, len(bidEntries))
  2430  
  2431  		// m2: Add a bid from m2 for fun.
  2432  		_createNFTBidWithTestMeta(
  2433  			testMeta,
  2434  			10, /*FeeRateNanosPerKB*/
  2435  			m2Pub,
  2436  			m2Priv,
  2437  			post1Hash,
  2438  			1,    /*SerialNumber*/
  2439  			1112, /*BidAmountNanos*/
  2440  		)
  2441  
  2442  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  2443  		require.Equal(2, len(bidEntries))
  2444  
  2445  		// m3: Add a blanket bid from m3 for fun.
  2446  		_createNFTBidWithTestMeta(
  2447  			testMeta,
  2448  			10, /*FeeRateNanosPerKB*/
  2449  			m3Pub,
  2450  			m3Priv,
  2451  			post1Hash,
  2452  			1,    /*SerialNumber*/
  2453  			1113, /*BidAmountNanos*/
  2454  		)
  2455  
  2456  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  2457  		require.Equal(3, len(bidEntries))
  2458  	}
  2459  
  2460  	// TODO: add test to withdraw bid with a 0 BidAmountNanos
  2461  
  2462  	// Accept m3's bid on #1 and m1's blanked bid on #2, weeeee!
  2463  	{
  2464  		// Balance before.
  2465  		m0BalBefore := _getBalance(t, chain, nil, m0Pub)
  2466  		require.Equal(uint64(14959), m0BalBefore)
  2467  
  2468  		// This will accept m3's serial #1 bid.
  2469  		_acceptNFTBidWithTestMeta(
  2470  			testMeta,
  2471  			10, /*FeeRateNanosPerKB*/
  2472  			m0Pub,
  2473  			m0Priv,
  2474  			post1Hash,
  2475  			1,     /*SerialNumber*/
  2476  			m3Pub, /*bidderPkBase58Check*/
  2477  			1113,  /*bidAmountNanos*/
  2478  			"",    /*UnencryptedUnlockableText*/
  2479  		)
  2480  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  2481  		require.Equal(0, len(bidEntries))
  2482  
  2483  		// This will accept m1's serial #0 bid.
  2484  		_acceptNFTBidWithTestMeta(
  2485  			testMeta,
  2486  			10, /*FeeRateNanosPerKB*/
  2487  			m0Pub,
  2488  			m0Priv,
  2489  			post1Hash,
  2490  			2,     /*SerialNumber*/
  2491  			m1Pub, /*bidderPkBase58Check*/
  2492  			10,    /*bidAmountNanos*/
  2493  			"",    /*UnencryptedUnlockableText*/
  2494  		)
  2495  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 0)
  2496  		require.Equal(0, len(bidEntries))
  2497  
  2498  		// This NFT doesn't have royalties so m0's balance should be directly related to the bids accepted.
  2499  		m0BalAfter := _getBalance(t, chain, nil, m0Pub)
  2500  		require.Equal(m0BalBefore-4+1113+10, m0BalAfter)
  2501  		require.Equal(uint64(16078), m0BalAfter)
  2502  	}
  2503  
  2504  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  2505  	_rollBackTestMetaTxnsAndFlush(testMeta)
  2506  	_applyTestMetaTxnsToMempool(testMeta)
  2507  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  2508  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  2509  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  2510  }
  2511  
  2512  // Test to make sure an NFT created with "IsForSale=false" does not accept bids.
  2513  func TestNFTCreatedIsNotForSale(t *testing.T) {
  2514  	assert := assert.New(t)
  2515  	require := require.New(t)
  2516  	_ = assert
  2517  	_ = require
  2518  
  2519  	chain, params, db := NewLowDifficultyBlockchain()
  2520  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  2521  	// Make m3, m4 a paramUpdater for this test
  2522  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  2523  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  2524  
  2525  	// Mine a few blocks to give the senderPkString some money.
  2526  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2527  	require.NoError(err)
  2528  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2529  	require.NoError(err)
  2530  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2531  	require.NoError(err)
  2532  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2533  	require.NoError(err)
  2534  
  2535  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  2536  	testMeta := &TestMeta{
  2537  		t:           t,
  2538  		chain:       chain,
  2539  		params:      params,
  2540  		db:          db,
  2541  		mempool:     mempool,
  2542  		miner:       miner,
  2543  		savedHeight: chain.blockTip().Height + 1,
  2544  	}
  2545  
  2546  	// Fund all the keys.
  2547  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 15000)
  2548  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 15000)
  2549  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 15000)
  2550  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 15000)
  2551  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  2552  
  2553  	// Set max copies to a non-zero value to activate NFTs.
  2554  	{
  2555  		_updateGlobalParamsEntryWithTestMeta(
  2556  			testMeta,
  2557  			10, /*FeeRateNanosPerKB*/
  2558  			m4Pub,
  2559  			m4Priv,
  2560  			-1, -1, -1, -1,
  2561  			1000, /*maxCopiesPerNFT*/
  2562  		)
  2563  	}
  2564  
  2565  	// Create a simple post.
  2566  	{
  2567  		_submitPostWithTestMeta(
  2568  			testMeta,
  2569  			10,                                 /*feeRateNanosPerKB*/
  2570  			m0Pub,                              /*updaterPkBase58Check*/
  2571  			m0Priv,                             /*updaterPrivBase58Check*/
  2572  			[]byte{},                           /*postHashToModify*/
  2573  			[]byte{},                           /*parentStakeID*/
  2574  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  2575  			[]byte{},
  2576  			1502947011*1e9, /*tstampNanos*/
  2577  			false /*isHidden*/)
  2578  	}
  2579  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  2580  
  2581  	// Create a profile so me can make an NFT.
  2582  	{
  2583  		_updateProfileWithTestMeta(
  2584  			testMeta,
  2585  			10,            /*feeRateNanosPerKB*/
  2586  			m0Pub,         /*updaterPkBase58Check*/
  2587  			m0Priv,        /*updaterPrivBase58Check*/
  2588  			[]byte{},      /*profilePubKey*/
  2589  			"m2",          /*newUsername*/
  2590  			"i am the m2", /*newDescription*/
  2591  			shortPic,      /*newProfilePic*/
  2592  			10*100,        /*newCreatorBasisPoints*/
  2593  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  2594  			false /*isHidden*/)
  2595  	}
  2596  
  2597  	// Create NFT with IsForSale=false.
  2598  	{
  2599  		// Balance before.
  2600  		m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub)
  2601  		require.Equal(uint64(14960), m0BalBeforeNFT)
  2602  
  2603  		_createNFTWithTestMeta(
  2604  			testMeta,
  2605  			10, /*FeeRateNanosPerKB*/
  2606  			m0Pub,
  2607  			m0Priv,
  2608  			post1Hash,
  2609  			100,   /*NumCopies*/
  2610  			false, /*HasUnlockable*/
  2611  			false, /*IsForSale*/
  2612  			0,     /*MinBidAmountNanos*/
  2613  			0,     /*nftFee*/
  2614  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  2615  			0,     /*nftRoyaltyToCoinBasisPoints*/
  2616  		)
  2617  
  2618  		// Balance after. Since the default NFT fee is 0, m0 is only charged the nanos per kb fee.
  2619  		m0BalAfterNFT := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
  2620  		require.Equal(uint64(14959), m0BalAfterNFT)
  2621  	}
  2622  
  2623  	// Error case: Attempt to make some bids on an NFT that is not for sale, they should error.
  2624  	{
  2625  		_, _, _, err := _createNFTBid(
  2626  			t, chain, db, params, 10,
  2627  			m1Pub,
  2628  			m1Priv,
  2629  			post1Hash,
  2630  			1,    /*SerialNumber*/
  2631  			1000, /*BidAmountNanos*/
  2632  		)
  2633  		require.Error(err)
  2634  		require.Contains(err.Error(), RuleErrorNFTBidOnNFTThatIsNotForSale)
  2635  
  2636  		// None of the serial numbers should accept bids.
  2637  		_, _, _, err = _createNFTBid(
  2638  			t, chain, db, params, 10,
  2639  			m1Pub,
  2640  			m1Priv,
  2641  			post1Hash,
  2642  			99,   /*SerialNumber*/
  2643  			1000, /*BidAmountNanos*/
  2644  		)
  2645  		require.Error(err)
  2646  		require.Contains(err.Error(), RuleErrorNFTBidOnNFTThatIsNotForSale)
  2647  	}
  2648  
  2649  	// Update <post1, #1>, so that it is for sale.
  2650  	{
  2651  		_updateNFTWithTestMeta(
  2652  			testMeta,
  2653  			10, /*FeeRateNanosPerKB*/
  2654  			m0Pub,
  2655  			m0Priv,
  2656  			post1Hash,
  2657  			1,    /*SerialNumber*/
  2658  			true, /*IsForSale*/
  2659  			0,    /*MinBidAmountNanos*/
  2660  		)
  2661  	}
  2662  
  2663  	// Now that <post1, #1> is for sale, creating a bid should work.
  2664  	{
  2665  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  2666  		require.Equal(0, len(bidEntries))
  2667  
  2668  		// m1 --> <post1, #1>
  2669  		_createNFTBidWithTestMeta(
  2670  			testMeta,
  2671  			10, /*FeeRateNanosPerKB*/
  2672  			m1Pub,
  2673  			m1Priv,
  2674  			post1Hash,
  2675  			1,    /*SerialNumber*/
  2676  			1111, /*BidAmountNanos*/
  2677  		)
  2678  
  2679  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  2680  		require.Equal(1, len(bidEntries))
  2681  	}
  2682  
  2683  	// Accept m1's bid on #1, weeeee!
  2684  	{
  2685  		// Balance before.
  2686  		m0BalBefore := _getBalance(t, chain, nil, m0Pub)
  2687  		require.Equal(uint64(14958), m0BalBefore)
  2688  
  2689  		// This will accept m1's serial #1 bid.
  2690  		_acceptNFTBidWithTestMeta(
  2691  			testMeta,
  2692  			10, /*FeeRateNanosPerKB*/
  2693  			m0Pub,
  2694  			m0Priv,
  2695  			post1Hash,
  2696  			1,     /*SerialNumber*/
  2697  			m1Pub, /*bidderPkBase58Check*/
  2698  			1111,  /*bidAmountNanos*/
  2699  			"",    /*UnencryptedUnlockableText*/
  2700  		)
  2701  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  2702  		require.Equal(0, len(bidEntries))
  2703  
  2704  		// This NFT doesn't have royalties so m0's balance should be directly related to the bids accepted.
  2705  		m0BalAfter := _getBalance(t, chain, nil, m0Pub)
  2706  		require.Equal(m0BalBefore-2+1111, m0BalAfter)
  2707  		require.Equal(uint64(16067), m0BalAfter)
  2708  	}
  2709  
  2710  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  2711  	_rollBackTestMetaTxnsAndFlush(testMeta)
  2712  	_applyTestMetaTxnsToMempool(testMeta)
  2713  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  2714  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  2715  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  2716  }
  2717  
  2718  func TestNFTMoreErrorCases(t *testing.T) {
  2719  	// Error cases tested:
  2720  	// - CreatorBasisPoints is greater than max value
  2721  	// - CoinBasisPoints is greater than max value
  2722  	// - Test than an NFT can only be minted once.
  2723  	// - Test that you cannot AcceptNFTBid if nft is not for sale.
  2724  	// - Test that min bid amount is behaving correctly.
  2725  
  2726  	assert := assert.New(t)
  2727  	require := require.New(t)
  2728  	_ = assert
  2729  	_ = require
  2730  
  2731  	chain, params, db := NewLowDifficultyBlockchain()
  2732  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  2733  	// Make m3, m4 a paramUpdater for this test
  2734  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  2735  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  2736  
  2737  	// Mine a few blocks to give the senderPkString some money.
  2738  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2739  	require.NoError(err)
  2740  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2741  	require.NoError(err)
  2742  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2743  	require.NoError(err)
  2744  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  2745  	require.NoError(err)
  2746  
  2747  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  2748  	testMeta := &TestMeta{
  2749  		t:           t,
  2750  		chain:       chain,
  2751  		params:      params,
  2752  		db:          db,
  2753  		mempool:     mempool,
  2754  		miner:       miner,
  2755  		savedHeight: chain.blockTip().Height + 1,
  2756  	}
  2757  
  2758  	// Fund all the keys.
  2759  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 70)
  2760  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 420)
  2761  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 2000)
  2762  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 210)
  2763  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  2764  
  2765  	// Set max copies to a non-zero value to activate NFTs.
  2766  	{
  2767  		_updateGlobalParamsEntryWithTestMeta(
  2768  			testMeta,
  2769  			10, /*FeeRateNanosPerKB*/
  2770  			m4Pub,
  2771  			m4Priv,
  2772  			-1, -1, -1, -1,
  2773  			1000, /*maxCopiesPerNFT*/
  2774  		)
  2775  	}
  2776  
  2777  	// Create a simple post.
  2778  	{
  2779  		_submitPostWithTestMeta(
  2780  			testMeta,
  2781  			10,                                 /*feeRateNanosPerKB*/
  2782  			m0Pub,                              /*updaterPkBase58Check*/
  2783  			m0Priv,                             /*updaterPrivBase58Check*/
  2784  			[]byte{},                           /*postHashToModify*/
  2785  			[]byte{},                           /*parentStakeID*/
  2786  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  2787  			[]byte{},
  2788  			1502947011*1e9, /*tstampNanos*/
  2789  			false /*isHidden*/)
  2790  	}
  2791  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  2792  
  2793  	// Create a profile so we can make an NFT.
  2794  	{
  2795  		_updateProfileWithTestMeta(
  2796  			testMeta,
  2797  			10,            /*feeRateNanosPerKB*/
  2798  			m0Pub,         /*updaterPkBase58Check*/
  2799  			m0Priv,        /*updaterPrivBase58Check*/
  2800  			[]byte{},      /*profilePubKey*/
  2801  			"m2",          /*newUsername*/
  2802  			"i am the m2", /*newDescription*/
  2803  			shortPic,      /*newProfilePic*/
  2804  			10*100,        /*newCreatorBasisPoints*/
  2805  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  2806  			false /*isHidden*/)
  2807  	}
  2808  
  2809  	// Error case: CreatorBasisPoints / CoinBasisPoints greater than max.
  2810  	{
  2811  		_, _, _, err := _createNFT(
  2812  			t, chain, db, params, 10,
  2813  			m0Pub,
  2814  			m0Priv,
  2815  			post1Hash,
  2816  			100,   /*NumCopies*/
  2817  			false, /*HasUnlockable*/
  2818  			true,  /*IsForSale*/
  2819  			0,     /*MinBidAmountNanos*/
  2820  			0,     /*nftFee*/
  2821  			10001, /*nftRoyaltyToCreatorBasisPoints*/
  2822  			0,     /*nftRoyaltyToCoinBasisPoints*/
  2823  		)
  2824  
  2825  		require.Error(err)
  2826  		require.Contains(err.Error(), RuleErrorNFTRoyaltyHasTooManyBasisPoints)
  2827  
  2828  		_, _, _, err = _createNFT(
  2829  			t, chain, db, params, 10,
  2830  			m0Pub,
  2831  			m0Priv,
  2832  			post1Hash,
  2833  			100,   /*NumCopies*/
  2834  			false, /*HasUnlockable*/
  2835  			true,  /*IsForSale*/
  2836  			0,     /*MinBidAmountNanos*/
  2837  			0,     /*nftFee*/
  2838  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  2839  			10001, /*nftRoyaltyToCoinBasisPoints*/
  2840  		)
  2841  
  2842  		require.Error(err)
  2843  		require.Contains(err.Error(), RuleErrorNFTRoyaltyHasTooManyBasisPoints)
  2844  
  2845  		_, _, _, err = _createNFT(
  2846  			t, chain, db, params, 10,
  2847  			m0Pub,
  2848  			m0Priv,
  2849  			post1Hash,
  2850  			100,   /*NumCopies*/
  2851  			false, /*HasUnlockable*/
  2852  			true,  /*IsForSale*/
  2853  			0,     /*MinBidAmountNanos*/
  2854  			0,     /*nftFee*/
  2855  			5001,  /*nftRoyaltyToCreatorBasisPoints*/
  2856  			5001,  /*nftRoyaltyToCoinBasisPoints*/
  2857  		)
  2858  
  2859  		require.Error(err)
  2860  		require.Contains(err.Error(), RuleErrorNFTRoyaltyHasTooManyBasisPoints)
  2861  	}
  2862  
  2863  	// Finally, have m0 turn post1 into an NFT. Woohoo!
  2864  	{
  2865  		// Balance before.
  2866  		m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub)
  2867  		require.Equal(uint64(30), m0BalBeforeNFT)
  2868  
  2869  		_createNFTWithTestMeta(
  2870  			testMeta,
  2871  			10, /*FeeRateNanosPerKB*/
  2872  			m0Pub,
  2873  			m0Priv,
  2874  			post1Hash,
  2875  			5,       /*NumCopies*/
  2876  			false,   /*HasUnlockable*/
  2877  			false,   /*IsForSale*/
  2878  			1000000, /*MinBidAmountNanos*/
  2879  			0,       /*nftFee*/
  2880  			0,       /*nftRoyaltyToCreatorBasisPoints*/
  2881  			0,       /*nftRoyaltyToCoinBasisPoints*/
  2882  		)
  2883  
  2884  		// Balance after. Since the default NFT fee is 0, m0 is only charged the nanos per kb fee.
  2885  		m0BalAfterNFT := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
  2886  		require.Equal(uint64(29), m0BalAfterNFT)
  2887  	}
  2888  
  2889  	// Error case: Cannot mint the NFT a second time.
  2890  	{
  2891  		_, _, _, err := _createNFT(
  2892  			t, chain, db, params, 10,
  2893  			m0Pub,
  2894  			m0Priv,
  2895  			post1Hash,
  2896  			5,       /*NumCopies*/
  2897  			false,   /*HasUnlockable*/
  2898  			false,   /*IsForSale*/
  2899  			1000000, /*MinBidAmountNanos*/
  2900  			0,       /*nftFee*/
  2901  			0,       /*nftRoyaltyToCreatorBasisPoints*/
  2902  			0,       /*nftRoyaltyToCoinBasisPoints*/
  2903  		)
  2904  
  2905  		require.Error(err)
  2906  		require.Contains(err.Error(), RuleErrorCreateNFTOnPostThatAlreadyIsNFT)
  2907  
  2908  		// Should behave the same if we change the NFT metadata.
  2909  		_, _, _, err = _createNFT(
  2910  			t, chain, db, params, 10,
  2911  			m0Pub,
  2912  			m0Priv,
  2913  			post1Hash,
  2914  			5,       /*NumCopies*/
  2915  			false,   /*HasUnlockable*/
  2916  			true,    /*IsForSale*/
  2917  			1000000, /*MinBidAmountNanos*/
  2918  			0,       /*nftFee*/
  2919  			0,       /*nftRoyaltyToCreatorBasisPoints*/
  2920  			0,       /*nftRoyaltyToCoinBasisPoints*/
  2921  		)
  2922  
  2923  		require.Error(err)
  2924  		require.Contains(err.Error(), RuleErrorCreateNFTOnPostThatAlreadyIsNFT)
  2925  
  2926  		// Should behave the same if we change the NFT metadata.
  2927  		_, _, _, err = _createNFT(
  2928  			t, chain, db, params, 10,
  2929  			m0Pub,
  2930  			m0Priv,
  2931  			post1Hash,
  2932  			5,     /*NumCopies*/
  2933  			false, /*HasUnlockable*/
  2934  			true,  /*IsForSale*/
  2935  			0,     /*MinBidAmountNanos*/
  2936  			0,     /*nftFee*/
  2937  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  2938  			0,     /*nftRoyaltyToCoinBasisPoints*/
  2939  		)
  2940  
  2941  		require.Error(err)
  2942  		require.Contains(err.Error(), RuleErrorCreateNFTOnPostThatAlreadyIsNFT)
  2943  	}
  2944  
  2945  	// Have m1 make a standing offer on post1.
  2946  	{
  2947  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 0)
  2948  		require.Equal(0, len(bidEntries))
  2949  
  2950  		_createNFTBidWithTestMeta(
  2951  			testMeta,
  2952  			10, /*FeeRateNanosPerKB*/
  2953  			m1Pub,
  2954  			m1Priv,
  2955  			post1Hash,
  2956  			0, /*SerialNumber*/
  2957  			5, /*BidAmountNanos*/
  2958  		)
  2959  
  2960  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 0)
  2961  		require.Equal(1, len(bidEntries))
  2962  	}
  2963  
  2964  	// Error case: cannot accept a bid if the NFT is not for sale.
  2965  	{
  2966  		_, _, _, err = _acceptNFTBid(
  2967  			t, chain, db, params, 10,
  2968  			m1Pub,
  2969  			m1Priv,
  2970  			post1Hash,
  2971  			1, /*SerialNumber*/
  2972  			m1Pub,
  2973  			5,  /*BidAmountNanos*/
  2974  			"", /*UnlockableText*/
  2975  		)
  2976  		require.Error(err)
  2977  		require.Contains(err.Error(), RuleErrorNFTBidOnNFTThatIsNotForSale)
  2978  	}
  2979  
  2980  	// Update <post1, #1>, so that it is on sale.
  2981  	{
  2982  		_updateNFTWithTestMeta(
  2983  			testMeta,
  2984  			10, /*FeeRateNanosPerKB*/
  2985  			m0Pub,
  2986  			m0Priv,
  2987  			post1Hash,
  2988  			1,    /*SerialNumber*/
  2989  			true, /*IsForSale*/
  2990  			1000, /*MinBidAmountNanos*/
  2991  		)
  2992  	}
  2993  
  2994  	// Error case: make sure the min bid amount behaves correctly.
  2995  	{
  2996  		// You should not be able to create an NFT bid below the min bid amount.
  2997  		_, _, _, err := _createNFTBid(
  2998  			t, chain, db, params, 10,
  2999  			m1Pub,
  3000  			m1Priv,
  3001  			post1Hash,
  3002  			1, /*SerialNumber*/
  3003  			1, /*BidAmountNanos*/
  3004  		)
  3005  		require.Error(err)
  3006  		require.Contains(err.Error(), RuleErrorNFTBidLessThanMinBidAmountNanos)
  3007  	}
  3008  
  3009  	// A bid above the min bid amount should succeed.
  3010  	{
  3011  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  3012  		require.Equal(0, len(bidEntries))
  3013  
  3014  		_createNFTBidWithTestMeta(
  3015  			testMeta,
  3016  			10, /*FeeRateNanosPerKB*/
  3017  			m2Pub,
  3018  			m2Priv,
  3019  			post1Hash,
  3020  			1,    /*SerialNumber*/
  3021  			1001, /*BidAmountNanos*/
  3022  		)
  3023  
  3024  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3025  		require.Equal(1, len(bidEntries))
  3026  	}
  3027  
  3028  	// Accept m1's standing offer for the post. This should succeed despite the min bid amount.
  3029  	{
  3030  		_acceptNFTBidWithTestMeta(
  3031  			testMeta,
  3032  			10, /*FeeRateNanosPerKB*/
  3033  			m0Pub,
  3034  			m0Priv,
  3035  			post1Hash,
  3036  			1, /*SerialNumber*/
  3037  			m1Pub,
  3038  			5,  /*BidAmountNanos*/
  3039  			"", /*UnlockableText*/
  3040  		)
  3041  
  3042  		// Make sure the entries in the DB were deleted.
  3043  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 0)
  3044  		require.Equal(0, len(bidEntries))
  3045  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3046  		require.Equal(0, len(bidEntries))
  3047  	}
  3048  
  3049  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  3050  	_rollBackTestMetaTxnsAndFlush(testMeta)
  3051  	_applyTestMetaTxnsToMempool(testMeta)
  3052  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  3053  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  3054  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  3055  }
  3056  
  3057  func TestNFTBidsAreCanceledAfterAccept(t *testing.T) {
  3058  	assert := assert.New(t)
  3059  	require := require.New(t)
  3060  	_ = assert
  3061  	_ = require
  3062  
  3063  	chain, params, db := NewLowDifficultyBlockchain()
  3064  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  3065  	// Make m3, m4 a paramUpdater for this test
  3066  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  3067  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  3068  
  3069  	// Mine a few blocks to give the senderPkString some money.
  3070  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3071  	require.NoError(err)
  3072  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3073  	require.NoError(err)
  3074  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3075  	require.NoError(err)
  3076  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3077  	require.NoError(err)
  3078  
  3079  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  3080  	testMeta := &TestMeta{
  3081  		t:           t,
  3082  		chain:       chain,
  3083  		params:      params,
  3084  		db:          db,
  3085  		mempool:     mempool,
  3086  		miner:       miner,
  3087  		savedHeight: chain.blockTip().Height + 1,
  3088  	}
  3089  
  3090  	// Fund all the keys.
  3091  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 2000)
  3092  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 2000)
  3093  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 2000)
  3094  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 2000)
  3095  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  3096  
  3097  	// Set max copies to a non-zero value to activate NFTs.
  3098  	{
  3099  		_updateGlobalParamsEntryWithTestMeta(
  3100  			testMeta,
  3101  			10, /*FeeRateNanosPerKB*/
  3102  			m4Pub,
  3103  			m4Priv,
  3104  			-1, -1, -1, -1,
  3105  			1000, /*maxCopiesPerNFT*/
  3106  		)
  3107  	}
  3108  
  3109  	// Create a simple post.
  3110  	{
  3111  		_submitPostWithTestMeta(
  3112  			testMeta,
  3113  			10,                                 /*feeRateNanosPerKB*/
  3114  			m0Pub,                              /*updaterPkBase58Check*/
  3115  			m0Priv,                             /*updaterPrivBase58Check*/
  3116  			[]byte{},                           /*postHashToModify*/
  3117  			[]byte{},                           /*parentStakeID*/
  3118  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  3119  			[]byte{},
  3120  			1502947011*1e9, /*tstampNanos*/
  3121  			false /*isHidden*/)
  3122  	}
  3123  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  3124  
  3125  	// Create a profile so we can make an NFT.
  3126  	{
  3127  		_updateProfileWithTestMeta(
  3128  			testMeta,
  3129  			10,            /*feeRateNanosPerKB*/
  3130  			m0Pub,         /*updaterPkBase58Check*/
  3131  			m0Priv,        /*updaterPrivBase58Check*/
  3132  			[]byte{},      /*profilePubKey*/
  3133  			"m2",          /*newUsername*/
  3134  			"i am the m2", /*newDescription*/
  3135  			shortPic,      /*newProfilePic*/
  3136  			10*100,        /*newCreatorBasisPoints*/
  3137  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  3138  			false /*isHidden*/)
  3139  	}
  3140  
  3141  	// Finally, have m0 turn post1 into an NFT. Woohoo!
  3142  	{
  3143  		_createNFTWithTestMeta(
  3144  			testMeta,
  3145  			10, /*FeeRateNanosPerKB*/
  3146  			m0Pub,
  3147  			m0Priv,
  3148  			post1Hash,
  3149  			5,     /*NumCopies*/
  3150  			false, /*HasUnlockable*/
  3151  			true,  /*IsForSale*/
  3152  			10,    /*MinBidAmountNanos*/
  3153  			0,     /*nftFee*/
  3154  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  3155  			0,     /*nftRoyaltyToCoinBasisPoints*/
  3156  		)
  3157  	}
  3158  
  3159  	// Have m1, m2, and m3 all make some bids on the post.
  3160  	{
  3161  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  3162  		require.Equal(0, len(bidEntries))
  3163  
  3164  		_createNFTBidWithTestMeta(
  3165  			testMeta,
  3166  			10, /*FeeRateNanosPerKB*/
  3167  			m1Pub,
  3168  			m1Priv,
  3169  			post1Hash,
  3170  			1,  /*SerialNumber*/
  3171  			10, /*BidAmountNanos*/
  3172  		)
  3173  
  3174  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3175  		require.Equal(1, len(bidEntries))
  3176  
  3177  		_createNFTBidWithTestMeta(
  3178  			testMeta,
  3179  			10, /*FeeRateNanosPerKB*/
  3180  			m2Pub,
  3181  			m2Priv,
  3182  			post1Hash,
  3183  			1,  /*SerialNumber*/
  3184  			11, /*BidAmountNanos*/
  3185  		)
  3186  
  3187  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3188  		require.Equal(2, len(bidEntries))
  3189  
  3190  		_createNFTBidWithTestMeta(
  3191  			testMeta,
  3192  			10, /*FeeRateNanosPerKB*/
  3193  			m1Pub,
  3194  			m1Priv,
  3195  			post1Hash,
  3196  			1,  /*SerialNumber*/
  3197  			12, /*BidAmountNanos*/
  3198  		)
  3199  
  3200  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3201  		require.Equal(2, len(bidEntries))
  3202  
  3203  		_createNFTBidWithTestMeta(
  3204  			testMeta,
  3205  			10, /*FeeRateNanosPerKB*/
  3206  			m3Pub,
  3207  			m3Priv,
  3208  			post1Hash,
  3209  			1,  /*SerialNumber*/
  3210  			13, /*BidAmountNanos*/
  3211  		)
  3212  
  3213  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3214  		require.Equal(3, len(bidEntries))
  3215  
  3216  		_createNFTBidWithTestMeta(
  3217  			testMeta,
  3218  			10, /*FeeRateNanosPerKB*/
  3219  			m2Pub,
  3220  			m2Priv,
  3221  			post1Hash,
  3222  			1,  /*SerialNumber*/
  3223  			14, /*BidAmountNanos*/
  3224  		)
  3225  
  3226  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3227  		require.Equal(3, len(bidEntries))
  3228  
  3229  		_createNFTBidWithTestMeta(
  3230  			testMeta,
  3231  			10, /*FeeRateNanosPerKB*/
  3232  			m3Pub,
  3233  			m3Priv,
  3234  			post1Hash,
  3235  			1,  /*SerialNumber*/
  3236  			15, /*BidAmountNanos*/
  3237  		)
  3238  
  3239  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3240  		require.Equal(3, len(bidEntries))
  3241  
  3242  		_createNFTBidWithTestMeta(
  3243  			testMeta,
  3244  			10, /*FeeRateNanosPerKB*/
  3245  			m2Pub,
  3246  			m2Priv,
  3247  			post1Hash,
  3248  			1,  /*SerialNumber*/
  3249  			16, /*BidAmountNanos*/
  3250  		)
  3251  
  3252  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3253  		require.Equal(3, len(bidEntries))
  3254  
  3255  		_createNFTBidWithTestMeta(
  3256  			testMeta,
  3257  			10, /*FeeRateNanosPerKB*/
  3258  			m3Pub,
  3259  			m3Priv,
  3260  			post1Hash,
  3261  			1,  /*SerialNumber*/
  3262  			17, /*BidAmountNanos*/
  3263  		)
  3264  
  3265  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  3266  		require.Equal(3, len(bidEntries))
  3267  	}
  3268  
  3269  	// Error case: cannot accept an old bid (m1 made a bid of 10 nanos, which was later updated).
  3270  	{
  3271  		_, _, _, err = _acceptNFTBid(
  3272  			t, chain, db, params, 10,
  3273  			m0Pub,
  3274  			m0Priv,
  3275  			post1Hash,
  3276  			1, /*SerialNumber*/
  3277  			m1Pub,
  3278  			10, /*BidAmountNanos*/
  3279  			"", /*UnlockableText*/
  3280  		)
  3281  		require.Error(err)
  3282  		require.Contains(err.Error(), RuleErrorAcceptedNFTBidAmountDoesNotMatch)
  3283  	}
  3284  
  3285  	// Accept m2's bid on the post. Make sure all bids are deleted.
  3286  	{
  3287  		_acceptNFTBidWithTestMeta(
  3288  			testMeta,
  3289  			10, /*FeeRateNanosPerKB*/
  3290  			m0Pub,
  3291  			m0Priv,
  3292  			post1Hash,
  3293  			1, /*SerialNumber*/
  3294  			m2Pub,
  3295  			16, /*BidAmountNanos*/
  3296  			"", /*UnlockableText*/
  3297  		)
  3298  
  3299  		// Make sure the entries in the DB were deleted.
  3300  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  3301  		require.Equal(0, len(bidEntries))
  3302  	}
  3303  
  3304  	// Error case: accepting m1 or m3s bid should fail now.
  3305  	{
  3306  		_, _, _, err = _acceptNFTBid(
  3307  			t, chain, db, params, 10,
  3308  			m0Pub,
  3309  			m0Priv,
  3310  			post1Hash,
  3311  			1, /*SerialNumber*/
  3312  			m1Pub,
  3313  			12, /*BidAmountNanos*/
  3314  			"", /*UnlockableText*/
  3315  		)
  3316  		require.Error(err)
  3317  		require.Contains(err.Error(), RuleErrorNFTBidOnNFTThatIsNotForSale)
  3318  
  3319  		_, _, _, err = _acceptNFTBid(
  3320  			t, chain, db, params, 10,
  3321  			m0Pub,
  3322  			m0Priv,
  3323  			post1Hash,
  3324  			1, /*SerialNumber*/
  3325  			m1Pub,
  3326  			17, /*BidAmountNanos*/
  3327  			"", /*UnlockableText*/
  3328  		)
  3329  		require.Error(err)
  3330  		require.Contains(err.Error(), RuleErrorNFTBidOnNFTThatIsNotForSale)
  3331  	}
  3332  
  3333  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  3334  	_rollBackTestMetaTxnsAndFlush(testMeta)
  3335  	_applyTestMetaTxnsToMempool(testMeta)
  3336  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  3337  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  3338  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  3339  }
  3340  
  3341  func TestNFTDifferentMinBidAmountSerialNumbers(t *testing.T) {
  3342  	assert := assert.New(t)
  3343  	require := require.New(t)
  3344  	_ = assert
  3345  	_ = require
  3346  
  3347  	chain, params, db := NewLowDifficultyBlockchain()
  3348  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  3349  	// Make m3, m4 a paramUpdater for this test
  3350  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  3351  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  3352  
  3353  	// Mine a few blocks to give the senderPkString some money.
  3354  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3355  	require.NoError(err)
  3356  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3357  	require.NoError(err)
  3358  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3359  	require.NoError(err)
  3360  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3361  	require.NoError(err)
  3362  
  3363  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  3364  	testMeta := &TestMeta{
  3365  		t:           t,
  3366  		chain:       chain,
  3367  		params:      params,
  3368  		db:          db,
  3369  		mempool:     mempool,
  3370  		miner:       miner,
  3371  		savedHeight: chain.blockTip().Height + 1,
  3372  	}
  3373  
  3374  	// Fund all the keys.
  3375  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 2000)
  3376  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 2000)
  3377  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 2000)
  3378  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 2000)
  3379  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  3380  
  3381  	// Set max copies to a non-zero value to activate NFTs.
  3382  	{
  3383  		_updateGlobalParamsEntryWithTestMeta(
  3384  			testMeta,
  3385  			10, /*FeeRateNanosPerKB*/
  3386  			m4Pub,
  3387  			m4Priv,
  3388  			-1, -1, -1, -1,
  3389  			1000, /*maxCopiesPerNFT*/
  3390  		)
  3391  	}
  3392  
  3393  	// Create a simple post.
  3394  	{
  3395  		_submitPostWithTestMeta(
  3396  			testMeta,
  3397  			10,                                 /*feeRateNanosPerKB*/
  3398  			m0Pub,                              /*updaterPkBase58Check*/
  3399  			m0Priv,                             /*updaterPrivBase58Check*/
  3400  			[]byte{},                           /*postHashToModify*/
  3401  			[]byte{},                           /*parentStakeID*/
  3402  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  3403  			[]byte{},
  3404  			1502947011*1e9, /*tstampNanos*/
  3405  			false /*isHidden*/)
  3406  	}
  3407  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  3408  
  3409  	// Create a profile so we can make an NFT.
  3410  	{
  3411  		_updateProfileWithTestMeta(
  3412  			testMeta,
  3413  			10,            /*feeRateNanosPerKB*/
  3414  			m0Pub,         /*updaterPkBase58Check*/
  3415  			m0Priv,        /*updaterPrivBase58Check*/
  3416  			[]byte{},      /*profilePubKey*/
  3417  			"m2",          /*newUsername*/
  3418  			"i am the m2", /*newDescription*/
  3419  			shortPic,      /*newProfilePic*/
  3420  			10*100,        /*newCreatorBasisPoints*/
  3421  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  3422  			false /*isHidden*/)
  3423  	}
  3424  
  3425  	// Finally, have m0 turn post1 into an NFT. Woohoo!
  3426  	{
  3427  		_createNFTWithTestMeta(
  3428  			testMeta,
  3429  			10, /*FeeRateNanosPerKB*/
  3430  			m0Pub,
  3431  			m0Priv,
  3432  			post1Hash,
  3433  			5,     /*NumCopies*/
  3434  			false, /*HasUnlockable*/
  3435  			false, /*IsForSale*/
  3436  			0,     /*MinBidAmountNanos*/
  3437  			0,     /*nftFee*/
  3438  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  3439  			0,     /*nftRoyaltyToCoinBasisPoints*/
  3440  		)
  3441  	}
  3442  
  3443  	// Update the post 1 NFTs, so that they have different min bid amounts.
  3444  	{
  3445  		_updateNFTWithTestMeta(
  3446  			testMeta,
  3447  			10, /*FeeRateNanosPerKB*/
  3448  			m0Pub,
  3449  			m0Priv,
  3450  			post1Hash,
  3451  			1,    /*SerialNumber*/
  3452  			true, /*IsForSale*/
  3453  			100,  /*MinBidAmountNanos*/
  3454  		)
  3455  
  3456  		_updateNFTWithTestMeta(
  3457  			testMeta,
  3458  			10, /*FeeRateNanosPerKB*/
  3459  			m0Pub,
  3460  			m0Priv,
  3461  			post1Hash,
  3462  			2,    /*SerialNumber*/
  3463  			true, /*IsForSale*/
  3464  			300,  /*MinBidAmountNanos*/
  3465  		)
  3466  
  3467  		_updateNFTWithTestMeta(
  3468  			testMeta,
  3469  			10, /*FeeRateNanosPerKB*/
  3470  			m0Pub,
  3471  			m0Priv,
  3472  			post1Hash,
  3473  			3,    /*SerialNumber*/
  3474  			true, /*IsForSale*/
  3475  			500,  /*MinBidAmountNanos*/
  3476  		)
  3477  
  3478  		_updateNFTWithTestMeta(
  3479  			testMeta,
  3480  			10, /*FeeRateNanosPerKB*/
  3481  			m0Pub,
  3482  			m0Priv,
  3483  			post1Hash,
  3484  			4,    /*SerialNumber*/
  3485  			true, /*IsForSale*/
  3486  			400,  /*MinBidAmountNanos*/
  3487  		)
  3488  
  3489  		_updateNFTWithTestMeta(
  3490  			testMeta,
  3491  			10, /*FeeRateNanosPerKB*/
  3492  			m0Pub,
  3493  			m0Priv,
  3494  			post1Hash,
  3495  			5,    /*SerialNumber*/
  3496  			true, /*IsForSale*/
  3497  			200,  /*MinBidAmountNanos*/
  3498  		)
  3499  	}
  3500  
  3501  	// Error case: check that all the serial numbers error below the min bid amount as expected.
  3502  	{
  3503  		_, _, _, err := _createNFTBid(
  3504  			t, chain, db, params, 10,
  3505  			m1Pub,
  3506  			m1Priv,
  3507  			post1Hash,
  3508  			1,  /*SerialNumber*/
  3509  			99, /*BidAmountNanos*/
  3510  		)
  3511  		require.Error(err)
  3512  		require.Contains(err.Error(), RuleErrorNFTBidLessThanMinBidAmountNanos)
  3513  
  3514  		_, _, _, err = _createNFTBid(
  3515  			t, chain, db, params, 10,
  3516  			m2Pub,
  3517  			m2Priv,
  3518  			post1Hash,
  3519  			2,   /*SerialNumber*/
  3520  			299, /*BidAmountNanos*/
  3521  		)
  3522  		require.Error(err)
  3523  		require.Contains(err.Error(), RuleErrorNFTBidLessThanMinBidAmountNanos)
  3524  
  3525  		_, _, _, err = _createNFTBid(
  3526  			t, chain, db, params, 10,
  3527  			m3Pub,
  3528  			m3Priv,
  3529  			post1Hash,
  3530  			3,   /*SerialNumber*/
  3531  			499, /*BidAmountNanos*/
  3532  		)
  3533  		require.Error(err)
  3534  		require.Contains(err.Error(), RuleErrorNFTBidLessThanMinBidAmountNanos)
  3535  
  3536  		_, _, _, err = _createNFTBid(
  3537  			t, chain, db, params, 10,
  3538  			m2Pub,
  3539  			m2Priv,
  3540  			post1Hash,
  3541  			4,   /*SerialNumber*/
  3542  			399, /*BidAmountNanos*/
  3543  		)
  3544  		require.Error(err)
  3545  		require.Contains(err.Error(), RuleErrorNFTBidLessThanMinBidAmountNanos)
  3546  
  3547  		_, _, _, err = _createNFTBid(
  3548  			t, chain, db, params, 10,
  3549  			m1Pub,
  3550  			m1Priv,
  3551  			post1Hash,
  3552  			5,   /*SerialNumber*/
  3553  			199, /*BidAmountNanos*/
  3554  		)
  3555  		require.Error(err)
  3556  		require.Contains(err.Error(), RuleErrorNFTBidLessThanMinBidAmountNanos)
  3557  	}
  3558  
  3559  	// Bids at the min bid amount nanos threshold should not error.
  3560  	{
  3561  		_createNFTBidWithTestMeta(
  3562  			testMeta,
  3563  			10, /*FeeRateNanosPerKB*/
  3564  			m1Pub,
  3565  			m1Priv,
  3566  			post1Hash,
  3567  			1,   /*SerialNumber*/
  3568  			100, /*BidAmountNanos*/
  3569  		)
  3570  
  3571  		_createNFTBidWithTestMeta(
  3572  			testMeta,
  3573  			10, /*FeeRateNanosPerKB*/
  3574  			m1Pub,
  3575  			m1Priv,
  3576  			post1Hash,
  3577  			2,   /*SerialNumber*/
  3578  			300, /*BidAmountNanos*/
  3579  		)
  3580  
  3581  		_createNFTBidWithTestMeta(
  3582  			testMeta,
  3583  			10, /*FeeRateNanosPerKB*/
  3584  			m1Pub,
  3585  			m1Priv,
  3586  			post1Hash,
  3587  			3,   /*SerialNumber*/
  3588  			500, /*BidAmountNanos*/
  3589  		)
  3590  
  3591  		_createNFTBidWithTestMeta(
  3592  			testMeta,
  3593  			10, /*FeeRateNanosPerKB*/
  3594  			m1Pub,
  3595  			m1Priv,
  3596  			post1Hash,
  3597  			4,   /*SerialNumber*/
  3598  			400, /*BidAmountNanos*/
  3599  		)
  3600  
  3601  		_createNFTBidWithTestMeta(
  3602  			testMeta,
  3603  			10, /*FeeRateNanosPerKB*/
  3604  			m1Pub,
  3605  			m1Priv,
  3606  			post1Hash,
  3607  			5,   /*SerialNumber*/
  3608  			200, /*BidAmountNanos*/
  3609  		)
  3610  	}
  3611  
  3612  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  3613  	_rollBackTestMetaTxnsAndFlush(testMeta)
  3614  	_applyTestMetaTxnsToMempool(testMeta)
  3615  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  3616  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  3617  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  3618  }
  3619  
  3620  func TestNFTMaxCopiesGlobalParam(t *testing.T) {
  3621  	assert := assert.New(t)
  3622  	require := require.New(t)
  3623  	_ = assert
  3624  	_ = require
  3625  
  3626  	chain, params, db := NewLowDifficultyBlockchain()
  3627  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  3628  	// Make m3, m4 a paramUpdater for this test
  3629  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  3630  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  3631  
  3632  	// Mine a few blocks to give the senderPkString some money.
  3633  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3634  	require.NoError(err)
  3635  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3636  	require.NoError(err)
  3637  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3638  	require.NoError(err)
  3639  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3640  	require.NoError(err)
  3641  
  3642  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  3643  	testMeta := &TestMeta{
  3644  		t:           t,
  3645  		chain:       chain,
  3646  		params:      params,
  3647  		db:          db,
  3648  		mempool:     mempool,
  3649  		miner:       miner,
  3650  		savedHeight: chain.blockTip().Height + 1,
  3651  	}
  3652  
  3653  	// Fund all the keys.
  3654  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 1000)
  3655  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 1000)
  3656  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 1000)
  3657  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 1000)
  3658  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  3659  
  3660  	// Set max copies to a non-zero value to activate NFTs.
  3661  	{
  3662  		_updateGlobalParamsEntryWithTestMeta(
  3663  			testMeta,
  3664  			10, /*FeeRateNanosPerKB*/
  3665  			m4Pub,
  3666  			m4Priv,
  3667  			-1, -1, -1, -1,
  3668  			1000, /*maxCopiesPerNFT*/
  3669  		)
  3670  	}
  3671  
  3672  	// Create a couple posts to test NFT creation with.
  3673  	{
  3674  		_submitPostWithTestMeta(
  3675  			testMeta,
  3676  			10,                                 /*feeRateNanosPerKB*/
  3677  			m0Pub,                              /*updaterPkBase58Check*/
  3678  			m0Priv,                             /*updaterPrivBase58Check*/
  3679  			[]byte{},                           /*postHashToModify*/
  3680  			[]byte{},                           /*parentStakeID*/
  3681  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  3682  			[]byte{},
  3683  			1502947011*1e9, /*tstampNanos*/
  3684  			false /*isHidden*/)
  3685  
  3686  		_submitPostWithTestMeta(
  3687  			testMeta,
  3688  			10,                                 /*feeRateNanosPerKB*/
  3689  			m0Pub,                              /*updaterPkBase58Check*/
  3690  			m0Priv,                             /*updaterPrivBase58Check*/
  3691  			[]byte{},                           /*postHashToModify*/
  3692  			[]byte{},                           /*parentStakeID*/
  3693  			&DeSoBodySchema{Body: "m0 post 2"}, /*body*/
  3694  			[]byte{},
  3695  			1502947011*1e9, /*tstampNanos*/
  3696  			false /*isHidden*/)
  3697  
  3698  		_submitPostWithTestMeta(
  3699  			testMeta,
  3700  			10,                                 /*feeRateNanosPerKB*/
  3701  			m0Pub,                              /*updaterPkBase58Check*/
  3702  			m0Priv,                             /*updaterPrivBase58Check*/
  3703  			[]byte{},                           /*postHashToModify*/
  3704  			[]byte{},                           /*parentStakeID*/
  3705  			&DeSoBodySchema{Body: "m0 post 3"}, /*body*/
  3706  			[]byte{},
  3707  			1502947011*1e9, /*tstampNanos*/
  3708  			false /*isHidden*/)
  3709  	}
  3710  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  3711  	post2Hash := testMeta.txns[len(testMeta.txns)-2].Hash()
  3712  	post3Hash := testMeta.txns[len(testMeta.txns)-3].Hash()
  3713  
  3714  	// Create a profile so me can make an NFT.
  3715  	{
  3716  		_updateProfileWithTestMeta(
  3717  			testMeta,
  3718  			10,            /*feeRateNanosPerKB*/
  3719  			m0Pub,         /*updaterPkBase58Check*/
  3720  			m0Priv,        /*updaterPrivBase58Check*/
  3721  			[]byte{},      /*profilePubKey*/
  3722  			"m2",          /*newUsername*/
  3723  			"i am the m2", /*newDescription*/
  3724  			shortPic,      /*newProfilePic*/
  3725  			10*100,        /*newCreatorBasisPoints*/
  3726  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  3727  			false /*isHidden*/)
  3728  	}
  3729  
  3730  	// Error case: creating an NFT with 1001 copies should fail since the default max is 1000.
  3731  	{
  3732  		_, _, _, err := _createNFT(
  3733  			t, chain, db, params, 10,
  3734  			m0Pub,
  3735  			m0Priv,
  3736  			post1Hash,
  3737  			1001,  /*NumCopies*/
  3738  			false, /*HasUnlockable*/
  3739  			true,  /*IsForSale*/
  3740  			0,     /*MinBidAmountNanos*/
  3741  			0,     /*nftFee*/
  3742  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  3743  			0,     /*nftRoyaltyToCoinBasisPoints*/
  3744  		)
  3745  		require.Error(err)
  3746  		require.Contains(err.Error(), RuleErrorTooManyNFTCopies)
  3747  	}
  3748  
  3749  	// Make post 1 an NFT with 1000 copies, the default MaxCopiesPerNFT.
  3750  	{
  3751  		_createNFTWithTestMeta(
  3752  			testMeta,
  3753  			10, /*FeeRateNanosPerKB*/
  3754  			m0Pub,
  3755  			m0Priv,
  3756  			post1Hash,
  3757  			1000,  /*NumCopies*/
  3758  			false, /*HasUnlockable*/
  3759  			true,  /*IsForSale*/
  3760  			0,     /*MinBidAmountNanos*/
  3761  			0,     /*nftFee*/
  3762  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  3763  			0,     /*nftRoyaltyToCoinBasisPoints*/
  3764  		)
  3765  	}
  3766  
  3767  	// Now let's try making the MaxCopiesPerNFT ridiculously small.
  3768  	{
  3769  		_updateGlobalParamsEntryWithTestMeta(
  3770  			testMeta,
  3771  			10, /*FeeRateNanosPerKB*/
  3772  			m3Pub,
  3773  			m3Priv,
  3774  			-1, -1, -1, -1,
  3775  			1, /*maxCopiesPerNFT*/
  3776  		)
  3777  	}
  3778  
  3779  	// Error case: now creating an NFT with 2 copies should fail.
  3780  	{
  3781  		_, _, _, err := _createNFT(
  3782  			t, chain, db, params, 10,
  3783  			m0Pub,
  3784  			m0Priv,
  3785  			post2Hash,
  3786  			2,     /*NumCopies*/
  3787  			false, /*HasUnlockable*/
  3788  			true,  /*IsForSale*/
  3789  			0,     /*MinBidAmountNanos*/
  3790  			0,     /*nftFee*/
  3791  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  3792  			0,     /*nftRoyaltyToCoinBasisPoints*/
  3793  		)
  3794  		require.Error(err)
  3795  		require.Contains(err.Error(), RuleErrorTooManyNFTCopies)
  3796  	}
  3797  
  3798  	// Making an NFT with only 1 copy should succeed.
  3799  	{
  3800  		_createNFTWithTestMeta(
  3801  			testMeta,
  3802  			10, /*FeeRateNanosPerKB*/
  3803  			m0Pub,
  3804  			m0Priv,
  3805  			post2Hash,
  3806  			1,     /*NumCopies*/
  3807  			false, /*HasUnlockable*/
  3808  			true,  /*IsForSale*/
  3809  			0,     /*MinBidAmountNanos*/
  3810  			0,     /*nftFee*/
  3811  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  3812  			0,     /*nftRoyaltyToCoinBasisPoints*/
  3813  		)
  3814  	}
  3815  
  3816  	// Error case: setting MaxCopiesPerNFT to be >MaxMaxCopiesPerNFT or <MinMaxCopiesPerNFT should fail.
  3817  	{
  3818  		require.Equal(1, MinMaxCopiesPerNFT)
  3819  		require.Equal(10000, MaxMaxCopiesPerNFT)
  3820  
  3821  		_, _, _, err := _updateGlobalParamsEntry(
  3822  			testMeta.t, testMeta.chain, testMeta.db, testMeta.params,
  3823  			10, /*FeeRateNanosPerKB*/
  3824  			m3Pub,
  3825  			m3Priv,
  3826  			-1, -1, -1, -1,
  3827  			MaxMaxCopiesPerNFT+1, /*maxCopiesPerNFT*/
  3828  			true)                 /*flushToDB*/
  3829  		require.Error(err)
  3830  		require.Contains(err.Error(), RuleErrorMaxCopiesPerNFTTooHigh)
  3831  
  3832  		_, _, _, err = _updateGlobalParamsEntry(
  3833  			testMeta.t, testMeta.chain, testMeta.db, testMeta.params,
  3834  			10, /*FeeRateNanosPerKB*/
  3835  			m3Pub,
  3836  			m3Priv,
  3837  			-1, -1, -1, -1,
  3838  			MinMaxCopiesPerNFT-1, /*maxCopiesPerNFT*/
  3839  			true)                 /*flushToDB*/
  3840  		require.Error(err)
  3841  		require.Contains(err.Error(), RuleErrorMaxCopiesPerNFTTooLow)
  3842  	}
  3843  
  3844  	// Now let's try making the MaxCopiesPerNFT ridiculously large.
  3845  	{
  3846  		_updateGlobalParamsEntryWithTestMeta(
  3847  			testMeta,
  3848  			10, /*FeeRateNanosPerKB*/
  3849  			m3Pub,
  3850  			m3Priv,
  3851  			-1, -1, -1, -1,
  3852  			10000, /*maxCopiesPerNFT*/
  3853  		)
  3854  	}
  3855  
  3856  	// Making an NFT with 10000 copies should now be possible!
  3857  	{
  3858  		_createNFTWithTestMeta(
  3859  			testMeta,
  3860  			10, /*FeeRateNanosPerKB*/
  3861  			m0Pub,
  3862  			m0Priv,
  3863  			post3Hash,
  3864  			10000, /*NumCopies*/
  3865  			false, /*HasUnlockable*/
  3866  			true,  /*IsForSale*/
  3867  			0,     /*MinBidAmountNanos*/
  3868  			0,     /*nftFee*/
  3869  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  3870  			0,     /*nftRoyaltyToCoinBasisPoints*/
  3871  		)
  3872  	}
  3873  
  3874  	// Now place some bids to make sure the NFTs were really minted.
  3875  	{
  3876  		// Post 1 should have 1000 copies.
  3877  		dbEntries := DBGetNFTEntriesForPostHash(db, post1Hash)
  3878  		require.Equal(1000, len(dbEntries))
  3879  		_createNFTBidWithTestMeta(
  3880  			testMeta,
  3881  			10, /*FeeRateNanosPerKB*/
  3882  			m1Pub,
  3883  			m1Priv,
  3884  			post1Hash,
  3885  			1000, /*SerialNumber*/
  3886  			1,    /*BidAmountNanos*/
  3887  		)
  3888  
  3889  		// Post 2 should have 1 copy.
  3890  		dbEntries = DBGetNFTEntriesForPostHash(db, post2Hash)
  3891  		require.Equal(1, len(dbEntries))
  3892  		_createNFTBidWithTestMeta(
  3893  			testMeta,
  3894  			10, /*FeeRateNanosPerKB*/
  3895  			m2Pub,
  3896  			m2Priv,
  3897  			post2Hash,
  3898  			1, /*SerialNumber*/
  3899  			1, /*BidAmountNanos*/
  3900  		)
  3901  
  3902  		// Post 3 should have 10000 copies.
  3903  		dbEntries = DBGetNFTEntriesForPostHash(db, post3Hash)
  3904  		require.Equal(10000, len(dbEntries))
  3905  		_createNFTBidWithTestMeta(
  3906  			testMeta,
  3907  			10, /*FeeRateNanosPerKB*/
  3908  			m3Pub,
  3909  			m3Priv,
  3910  			post3Hash,
  3911  			10000, /*SerialNumber*/
  3912  			1,     /*BidAmountNanos*/
  3913  		)
  3914  	}
  3915  
  3916  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  3917  	_rollBackTestMetaTxnsAndFlush(testMeta)
  3918  	_applyTestMetaTxnsToMempool(testMeta)
  3919  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  3920  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  3921  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  3922  }
  3923  
  3924  func TestNFTPreviousOwnersCantAcceptBids(t *testing.T) {
  3925  	assert := assert.New(t)
  3926  	require := require.New(t)
  3927  	_ = assert
  3928  	_ = require
  3929  
  3930  	chain, params, db := NewLowDifficultyBlockchain()
  3931  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  3932  	// Make m3, m4 a paramUpdater for this test
  3933  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  3934  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  3935  
  3936  	// Mine a few blocks to give the senderPkString some money.
  3937  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3938  	require.NoError(err)
  3939  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3940  	require.NoError(err)
  3941  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3942  	require.NoError(err)
  3943  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  3944  	require.NoError(err)
  3945  
  3946  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  3947  	testMeta := &TestMeta{
  3948  		t:           t,
  3949  		chain:       chain,
  3950  		params:      params,
  3951  		db:          db,
  3952  		mempool:     mempool,
  3953  		miner:       miner,
  3954  		savedHeight: chain.blockTip().Height + 1,
  3955  	}
  3956  
  3957  	// Fund all the keys.
  3958  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 1000)
  3959  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 1000)
  3960  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 1000)
  3961  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 1000)
  3962  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  3963  
  3964  	// Set max copies to a non-zero value to activate NFTs.
  3965  	{
  3966  		_updateGlobalParamsEntryWithTestMeta(
  3967  			testMeta,
  3968  			10, /*FeeRateNanosPerKB*/
  3969  			m4Pub,
  3970  			m4Priv,
  3971  			-1, -1, -1, -1,
  3972  			1000, /*maxCopiesPerNFT*/
  3973  		)
  3974  	}
  3975  
  3976  	// Create a post for testing.
  3977  	{
  3978  		_submitPostWithTestMeta(
  3979  			testMeta,
  3980  			10,                                 /*feeRateNanosPerKB*/
  3981  			m0Pub,                              /*updaterPkBase58Check*/
  3982  			m0Priv,                             /*updaterPrivBase58Check*/
  3983  			[]byte{},                           /*postHashToModify*/
  3984  			[]byte{},                           /*parentStakeID*/
  3985  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  3986  			[]byte{},
  3987  			1502947011*1e9, /*tstampNanos*/
  3988  			false /*isHidden*/)
  3989  	}
  3990  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  3991  
  3992  	// NFT the post.
  3993  	{
  3994  		// You need a profile in order to create an NFT.
  3995  		_updateProfileWithTestMeta(
  3996  			testMeta,
  3997  			10,            /*feeRateNanosPerKB*/
  3998  			m0Pub,         /*updaterPkBase58Check*/
  3999  			m0Priv,        /*updaterPrivBase58Check*/
  4000  			[]byte{},      /*profilePubKey*/
  4001  			"m2",          /*newUsername*/
  4002  			"i am the m2", /*newDescription*/
  4003  			shortPic,      /*newProfilePic*/
  4004  			10*100,        /*newCreatorBasisPoints*/
  4005  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  4006  			false /*isHidden*/)
  4007  
  4008  		// We only need 1 copy for this test.
  4009  		_createNFTWithTestMeta(
  4010  			testMeta,
  4011  			10, /*FeeRateNanosPerKB*/
  4012  			m0Pub,
  4013  			m0Priv,
  4014  			post1Hash,
  4015  			1,     /*NumCopies*/
  4016  			false, /*HasUnlockable*/
  4017  			true,  /*IsForSale*/
  4018  			0,     /*MinBidAmountNanos*/
  4019  			0,     /*nftFee*/
  4020  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  4021  			0,     /*nftRoyaltyToCoinBasisPoints*/
  4022  		)
  4023  
  4024  		// Post 1 should have 1 copies.
  4025  		dbEntries := DBGetNFTEntriesForPostHash(db, post1Hash)
  4026  		require.Equal(1, len(dbEntries))
  4027  	}
  4028  
  4029  	// Have m1 place a bid and m0 accept it.
  4030  	{
  4031  		_createNFTBidWithTestMeta(
  4032  			testMeta,
  4033  			10, /*FeeRateNanosPerKB*/
  4034  			m1Pub,
  4035  			m1Priv,
  4036  			post1Hash,
  4037  			1, /*SerialNumber*/
  4038  			1, /*BidAmountNanos*/
  4039  		)
  4040  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  4041  		require.Equal(1, len(bidEntries))
  4042  
  4043  		_acceptNFTBidWithTestMeta(
  4044  			testMeta,
  4045  			10, /*FeeRateNanosPerKB*/
  4046  			m0Pub,
  4047  			m0Priv,
  4048  			post1Hash,
  4049  			1, /*SerialNumber*/
  4050  			m1Pub,
  4051  			1,  /*BidAmountNanos*/
  4052  			"", /*UnlockableText*/
  4053  		)
  4054  
  4055  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  4056  		require.Equal(0, len(bidEntries))
  4057  	}
  4058  
  4059  	// Error case: m0 should not be able to put m1's NFT for sale.
  4060  	{
  4061  		_, _, _, err := _updateNFT(
  4062  			t, chain, db, params, 10,
  4063  			m0Pub,
  4064  			m0Priv,
  4065  			post1Hash,
  4066  			1,     /*SerialNumber*/
  4067  			false, /*IsForSale*/
  4068  			0,     /*MinBidAmountNanos*/
  4069  		)
  4070  		require.Error(err)
  4071  		require.Contains(err.Error(), RuleErrorUpdateNFTByNonOwner)
  4072  	}
  4073  
  4074  	// Have m1 place the NFT for sale and m2 bid on it.
  4075  	{
  4076  		_updateNFTWithTestMeta(
  4077  			testMeta,
  4078  			10, /*FeeRateNanosPerKB*/
  4079  			m1Pub,
  4080  			m1Priv,
  4081  			post1Hash,
  4082  			1,    /*SerialNumber*/
  4083  			true, /*IsForSale*/
  4084  			0,    /*MinBidAmountNanos*/
  4085  		)
  4086  
  4087  		_createNFTBidWithTestMeta(
  4088  			testMeta,
  4089  			10, /*FeeRateNanosPerKB*/
  4090  			m2Pub,
  4091  			m2Priv,
  4092  			post1Hash,
  4093  			1, /*SerialNumber*/
  4094  			1, /*BidAmountNanos*/
  4095  		)
  4096  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  4097  		require.Equal(1, len(bidEntries))
  4098  	}
  4099  
  4100  	// Error case: m0 cannot accept the m2's bid on m1's behalf.
  4101  	{
  4102  		_, _, _, err = _acceptNFTBid(
  4103  			t, chain, db, params, 10,
  4104  			m0Pub,
  4105  			m0Priv,
  4106  			post1Hash,
  4107  			1, /*SerialNumber*/
  4108  			m2Pub,
  4109  			1,  /*BidAmountNanos*/
  4110  			"", /*UnlockableText*/
  4111  		)
  4112  		require.Error(err)
  4113  		require.Contains(err.Error(), RuleErrorAcceptNFTBidByNonOwner)
  4114  	}
  4115  
  4116  	// Have m1 accept the bid, m2 put the NFT for sale, and m3 bid on the NFT.
  4117  	{
  4118  		_acceptNFTBidWithTestMeta(
  4119  			testMeta,
  4120  			10, /*FeeRateNanosPerKB*/
  4121  			m1Pub,
  4122  			m1Priv,
  4123  			post1Hash,
  4124  			1, /*SerialNumber*/
  4125  			m2Pub,
  4126  			1,  /*BidAmountNanos*/
  4127  			"", /*UnlockableText*/
  4128  		)
  4129  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  4130  		require.Equal(0, len(bidEntries))
  4131  
  4132  		_updateNFTWithTestMeta(
  4133  			testMeta,
  4134  			10, /*FeeRateNanosPerKB*/
  4135  			m2Pub,
  4136  			m2Priv,
  4137  			post1Hash,
  4138  			1,    /*SerialNumber*/
  4139  			true, /*IsForSale*/
  4140  			0,    /*MinBidAmountNanos*/
  4141  		)
  4142  
  4143  		_createNFTBidWithTestMeta(
  4144  			testMeta,
  4145  			10, /*FeeRateNanosPerKB*/
  4146  			m3Pub,
  4147  			m3Priv,
  4148  			post1Hash,
  4149  			1, /*SerialNumber*/
  4150  			1, /*BidAmountNanos*/
  4151  		)
  4152  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  4153  		require.Equal(1, len(bidEntries))
  4154  	}
  4155  
  4156  	// Error case: m0 and m1 cannot accept the m3's bid on m2's behalf.
  4157  	{
  4158  		_, _, _, err = _acceptNFTBid(
  4159  			t, chain, db, params, 10,
  4160  			m0Pub,
  4161  			m0Priv,
  4162  			post1Hash,
  4163  			1, /*SerialNumber*/
  4164  			m3Pub,
  4165  			1,  /*BidAmountNanos*/
  4166  			"", /*UnlockableText*/
  4167  		)
  4168  		require.Error(err)
  4169  		require.Contains(err.Error(), RuleErrorAcceptNFTBidByNonOwner)
  4170  
  4171  		_, _, _, err = _acceptNFTBid(
  4172  			t, chain, db, params, 10,
  4173  			m1Pub,
  4174  			m1Priv,
  4175  			post1Hash,
  4176  			1, /*SerialNumber*/
  4177  			m3Pub,
  4178  			1,  /*BidAmountNanos*/
  4179  			"", /*UnlockableText*/
  4180  		)
  4181  		require.Error(err)
  4182  		require.Contains(err.Error(), RuleErrorAcceptNFTBidByNonOwner)
  4183  	}
  4184  
  4185  	// Have m2 accept the bid.
  4186  	{
  4187  		_acceptNFTBidWithTestMeta(
  4188  			testMeta,
  4189  			10, /*FeeRateNanosPerKB*/
  4190  			m2Pub,
  4191  			m2Priv,
  4192  			post1Hash,
  4193  			1, /*SerialNumber*/
  4194  			m3Pub,
  4195  			1,  /*BidAmountNanos*/
  4196  			"", /*UnlockableText*/
  4197  		)
  4198  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  4199  		require.Equal(0, len(bidEntries))
  4200  	}
  4201  
  4202  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  4203  	_rollBackTestMetaTxnsAndFlush(testMeta)
  4204  	_applyTestMetaTxnsToMempool(testMeta)
  4205  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  4206  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  4207  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  4208  }
  4209  
  4210  func TestNFTTransfersAndBurns(t *testing.T) {
  4211  	BrokenNFTBidsFixBlockHeight = uint32(0)
  4212  	NFTTransferOrBurnAndDerivedKeysBlockHeight = uint32(0)
  4213  
  4214  	assert := assert.New(t)
  4215  	require := require.New(t)
  4216  	_ = assert
  4217  	_ = require
  4218  
  4219  	chain, params, db := NewLowDifficultyBlockchain()
  4220  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  4221  	// Make m3 a paramUpdater for this test
  4222  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  4223  
  4224  	// Mine a few blocks to give the senderPkString some money.
  4225  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  4226  	require.NoError(err)
  4227  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  4228  	require.NoError(err)
  4229  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  4230  	require.NoError(err)
  4231  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  4232  	require.NoError(err)
  4233  
  4234  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  4235  	testMeta := &TestMeta{
  4236  		t:           t,
  4237  		chain:       chain,
  4238  		params:      params,
  4239  		db:          db,
  4240  		mempool:     mempool,
  4241  		miner:       miner,
  4242  		savedHeight: chain.blockTip().Height + 1,
  4243  	}
  4244  
  4245  	// Fund all the keys.
  4246  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 1000)
  4247  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 1000)
  4248  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 1000)
  4249  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 1000)
  4250  
  4251  	// Get PKIDs for checking nft ownership.
  4252  	m0PkBytes, _, err := Base58CheckDecode(m0Pub)
  4253  	require.NoError(err)
  4254  	m0PKID := DBGetPKIDEntryForPublicKey(db, m0PkBytes)
  4255  	_ = m0PKID
  4256  
  4257  	m1PkBytes, _, err := Base58CheckDecode(m1Pub)
  4258  	require.NoError(err)
  4259  	m1PKID := DBGetPKIDEntryForPublicKey(db, m1PkBytes)
  4260  	_ = m1PKID
  4261  
  4262  	m2PkBytes, _, err := Base58CheckDecode(m2Pub)
  4263  	require.NoError(err)
  4264  	m2PKID := DBGetPKIDEntryForPublicKey(db, m2PkBytes)
  4265  	_ = m2PKID
  4266  
  4267  	m3PkBytes, _, err := Base58CheckDecode(m3Pub)
  4268  	require.NoError(err)
  4269  	m3PKID := DBGetPKIDEntryForPublicKey(db, m3PkBytes)
  4270  	_ = m3PKID
  4271  
  4272  	// Set max copies to a non-zero value to activate NFTs.
  4273  	{
  4274  		_updateGlobalParamsEntryWithTestMeta(
  4275  			testMeta,
  4276  			10, /*FeeRateNanosPerKB*/
  4277  			m3Pub,
  4278  			m3Priv,
  4279  			-1, -1, -1, -1,
  4280  			1000, /*maxCopiesPerNFT*/
  4281  		)
  4282  	}
  4283  
  4284  	// Create two posts to NFTify (one will have unlockable, one will not).
  4285  	{
  4286  		_submitPostWithTestMeta(
  4287  			testMeta,
  4288  			10,                                 /*feeRateNanosPerKB*/
  4289  			m0Pub,                              /*updaterPkBase58Check*/
  4290  			m0Priv,                             /*updaterPrivBase58Check*/
  4291  			[]byte{},                           /*postHashToModify*/
  4292  			[]byte{},                           /*parentStakeID*/
  4293  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  4294  			[]byte{},
  4295  			1502947011*1e9, /*tstampNanos*/
  4296  			false /*isHidden*/)
  4297  
  4298  		_submitPostWithTestMeta(
  4299  			testMeta,
  4300  			10,                                 /*feeRateNanosPerKB*/
  4301  			m0Pub,                              /*updaterPkBase58Check*/
  4302  			m0Priv,                             /*updaterPrivBase58Check*/
  4303  			[]byte{},                           /*postHashToModify*/
  4304  			[]byte{},                           /*parentStakeID*/
  4305  			&DeSoBodySchema{Body: "m0 post 2"}, /*body*/
  4306  			[]byte{},
  4307  			1502947012*1e9, /*tstampNanos*/
  4308  			false /*isHidden*/)
  4309  	}
  4310  	post1Hash := testMeta.txns[len(testMeta.txns)-2].Hash()
  4311  	post2Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  4312  
  4313  	// Create a profile so we can make an NFT.
  4314  	{
  4315  		_updateProfileWithTestMeta(
  4316  			testMeta,
  4317  			10,            /*feeRateNanosPerKB*/
  4318  			m0Pub,         /*updaterPkBase58Check*/
  4319  			m0Priv,        /*updaterPrivBase58Check*/
  4320  			[]byte{},      /*profilePubKey*/
  4321  			"m0",          /*newUsername*/
  4322  			"i am the m0", /*newDescription*/
  4323  			shortPic,      /*newProfilePic*/
  4324  			10*100,        /*newCreatorBasisPoints*/
  4325  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  4326  			false /*isHidden*/)
  4327  	}
  4328  
  4329  	// Have m0 turn both post1 and post2 into NFTs.
  4330  	{
  4331  		// Balance before.
  4332  		m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub)
  4333  		require.Equal(uint64(959), m0BalBeforeNFT)
  4334  
  4335  		_createNFTWithTestMeta(
  4336  			testMeta,
  4337  			10, /*FeeRateNanosPerKB*/
  4338  			m0Pub,
  4339  			m0Priv,
  4340  			post1Hash,
  4341  			5,     /*NumCopies*/
  4342  			false, /*HasUnlockable*/
  4343  			true,  /*IsForSale*/
  4344  			0,     /*MinBidAmountNanos*/
  4345  			0,     /*nftFee*/
  4346  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  4347  			0,     /*nftRoyaltyToCoinBasisPoints*/
  4348  		)
  4349  
  4350  		_createNFTWithTestMeta(
  4351  			testMeta,
  4352  			10, /*FeeRateNanosPerKB*/
  4353  			m0Pub,
  4354  			m0Priv,
  4355  			post2Hash,
  4356  			5,     /*NumCopies*/
  4357  			true,  /*HasUnlockable*/
  4358  			false, /*IsForSale*/
  4359  			0,     /*MinBidAmountNanos*/
  4360  			0,     /*nftFee*/
  4361  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  4362  			0,     /*nftRoyaltyToCoinBasisPoints*/
  4363  		)
  4364  
  4365  		// Balance after. Since the default NFT fee is 0, m0 is only charged the nanos per kb fee.
  4366  		m0BalAfterNFT := _getBalance(testMeta.t, testMeta.chain, nil, m0Pub)
  4367  		require.Equal(uint64(957), m0BalAfterNFT)
  4368  	}
  4369  
  4370  	// Have m1 bid on and win post #1 / serial #5.
  4371  	{
  4372  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 5)
  4373  		require.Equal(0, len(bidEntries))
  4374  
  4375  		_createNFTBidWithTestMeta(
  4376  			testMeta,
  4377  			10, /*FeeRateNanosPerKB*/
  4378  			m1Pub,
  4379  			m1Priv,
  4380  			post1Hash,
  4381  			5, /*SerialNumber*/
  4382  			1, /*BidAmountNanos*/
  4383  		)
  4384  
  4385  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 5)
  4386  		require.Equal(1, len(bidEntries))
  4387  
  4388  		_acceptNFTBidWithTestMeta(
  4389  			testMeta,
  4390  			10, /*FeeRateNanosPerKB*/
  4391  			m0Pub,
  4392  			m0Priv,
  4393  			post1Hash,
  4394  			5, /*SerialNumber*/
  4395  			m1Pub,
  4396  			1,  /*BidAmountNanos*/
  4397  			"", /*UnlockableText*/
  4398  		)
  4399  
  4400  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 5)
  4401  		require.Equal(0, len(bidEntries))
  4402  	}
  4403  
  4404  	// Update <post1, #2>, so that it is no longer for sale.
  4405  	{
  4406  		_updateNFTWithTestMeta(
  4407  			testMeta,
  4408  			10, /*FeeRateNanosPerKB*/
  4409  			m0Pub,
  4410  			m0Priv,
  4411  			post1Hash,
  4412  			2,     /*SerialNumber*/
  4413  			false, /*IsForSale*/
  4414  			0,     /*MinBidAmountNanos*/
  4415  		)
  4416  	}
  4417  
  4418  	// At this point, we have 10 NFTs in the following state:
  4419  	//   - m1 owns <post 1, #5> (no unlockable, not for sale; purchased from m0)
  4420  	//   - m0 owns:
  4421  	//     - <post 1, #1-4> (no unlockable, all for sale except #2)
  4422  	//     - <post 2, #1-5> (has unlockable, none for sale)
  4423  
  4424  	// Now that we have some NFTs, let's try transferring them.
  4425  
  4426  	// Error case: non-existent NFT.
  4427  	{
  4428  		_, _, _, err := _transferNFT(
  4429  			t, chain, db, params, 10,
  4430  			m0Pub,
  4431  			m0Priv,
  4432  			m1Pub,
  4433  			post1Hash,
  4434  			6, /*Non-existent serial number.*/
  4435  			"",
  4436  		)
  4437  
  4438  		require.Error(err)
  4439  		require.Contains(err.Error(), RuleErrorCannotTransferNonExistentNFT)
  4440  	}
  4441  
  4442  	// Error case: transfer by non-owner.
  4443  	{
  4444  		_, _, _, err := _transferNFT(
  4445  			t, chain, db, params, 10,
  4446  			m3Pub,
  4447  			m3Priv,
  4448  			m2Pub,
  4449  			post1Hash,
  4450  			2,
  4451  			"",
  4452  		)
  4453  
  4454  		require.Error(err)
  4455  		require.Contains(err.Error(), RuleErrorNFTTransferByNonOwner)
  4456  	}
  4457  
  4458  	// Error case: cannot transfer NFT that is for sale.
  4459  	{
  4460  		_, _, _, err := _transferNFT(
  4461  			t, chain, db, params, 10,
  4462  			m0Pub,
  4463  			m0Priv,
  4464  			m1Pub,
  4465  			post1Hash,
  4466  			1,
  4467  			"",
  4468  		)
  4469  
  4470  		require.Error(err)
  4471  		require.Contains(err.Error(), RuleErrorCannotTransferForSaleNFT)
  4472  	}
  4473  
  4474  	// Error case: cannot transfer unlockable NFT without unlockable text.
  4475  	{
  4476  		_, _, _, err := _transferNFT(
  4477  			t, chain, db, params, 10,
  4478  			m0Pub,
  4479  			m0Priv,
  4480  			m1Pub,
  4481  			post2Hash,
  4482  			1,
  4483  			"",
  4484  		)
  4485  
  4486  		require.Error(err)
  4487  		require.Contains(err.Error(), RuleErrorCannotTransferUnlockableNFTWithoutUnlockable)
  4488  	}
  4489  
  4490  	// Let's transfer some NFTs!
  4491  	{
  4492  		// m0 transfers <post 1, #2> (not for sale, no unlockable) to m2.
  4493  		_transferNFTWithTestMeta(
  4494  			testMeta,
  4495  			10,
  4496  			m0Pub,
  4497  			m0Priv,
  4498  			m2Pub,
  4499  			post1Hash,
  4500  			2,
  4501  			"",
  4502  		)
  4503  
  4504  		// m1 transfers <post 1, #5> (not for sale, no unlockable) to m3.
  4505  		_transferNFTWithTestMeta(
  4506  			testMeta,
  4507  			10,
  4508  			m1Pub,
  4509  			m1Priv,
  4510  			m3Pub,
  4511  			post1Hash,
  4512  			5,
  4513  			"",
  4514  		)
  4515  
  4516  		// m0 transfers <post 2, #1> (not for sale, has unlockable) to m1.
  4517  		unlockableText := "this is an encrypted unlockable string"
  4518  		_transferNFTWithTestMeta(
  4519  			testMeta,
  4520  			10,
  4521  			m0Pub,
  4522  			m0Priv,
  4523  			m1Pub,
  4524  			post2Hash,
  4525  			1,
  4526  			unlockableText,
  4527  		)
  4528  
  4529  		// Check the state of the transferred NFTs.
  4530  		transferredNFT1 := DBGetNFTEntryByPostHashSerialNumber(db, post1Hash, 2)
  4531  		require.Equal(transferredNFT1.IsPending, true)
  4532  		require.True(reflect.DeepEqual(transferredNFT1.OwnerPKID, m2PKID.PKID))
  4533  		require.True(reflect.DeepEqual(transferredNFT1.LastOwnerPKID, m0PKID.PKID))
  4534  
  4535  		transferredNFT2 := DBGetNFTEntryByPostHashSerialNumber(db, post1Hash, 5)
  4536  		require.Equal(transferredNFT2.IsPending, true)
  4537  		require.True(reflect.DeepEqual(transferredNFT2.OwnerPKID, m3PKID.PKID))
  4538  		require.True(reflect.DeepEqual(transferredNFT2.LastOwnerPKID, m1PKID.PKID))
  4539  
  4540  		transferredNFT3 := DBGetNFTEntryByPostHashSerialNumber(db, post2Hash, 1)
  4541  		require.Equal(transferredNFT3.IsPending, true)
  4542  		require.True(reflect.DeepEqual(transferredNFT3.OwnerPKID, m1PKID.PKID))
  4543  		require.True(reflect.DeepEqual(transferredNFT3.LastOwnerPKID, m0PKID.PKID))
  4544  		require.True(reflect.DeepEqual(transferredNFT3.UnlockableText, []byte(unlockableText)))
  4545  	}
  4546  
  4547  	// Now let's test out accepting NFT transfers.
  4548  
  4549  	// Error case: non-existent NFT.
  4550  	{
  4551  		_, _, _, err := _acceptNFTTransfer(
  4552  			t, chain, db, params, 10,
  4553  			m0Pub,
  4554  			m0Priv,
  4555  			post1Hash,
  4556  			6, /*Non-existent serial number.*/
  4557  		)
  4558  
  4559  		require.Error(err)
  4560  		require.Contains(err.Error(), RuleErrorCannotAcceptTransferOfNonExistentNFT)
  4561  	}
  4562  
  4563  	// Error case: transfer by non-owner (m1 owns <post 2, #1>).
  4564  	{
  4565  		_, _, _, err := _acceptNFTTransfer(
  4566  			t, chain, db, params, 10,
  4567  			m0Pub,
  4568  			m0Priv,
  4569  			post2Hash,
  4570  			1,
  4571  		)
  4572  
  4573  		require.Error(err)
  4574  		require.Contains(err.Error(), RuleErrorAcceptNFTTransferByNonOwner)
  4575  	}
  4576  
  4577  	// Error case: cannot accept NFT transfer on non-pending NFT.
  4578  	{
  4579  		_, _, _, err := _acceptNFTTransfer(
  4580  			t, chain, db, params, 10,
  4581  			m0Pub,
  4582  			m0Priv,
  4583  			post2Hash,
  4584  			4,
  4585  		)
  4586  
  4587  		require.Error(err)
  4588  		require.Contains(err.Error(), RuleErrorAcceptNFTTransferForNonPendingNFT)
  4589  	}
  4590  
  4591  	// Let's accept some NFT transfers!
  4592  	{
  4593  		// m2 accepts <post 1, #2>
  4594  		_acceptNFTTransferWithTestMeta(
  4595  			testMeta,
  4596  			10,
  4597  			m2Pub,
  4598  			m2Priv,
  4599  			post1Hash,
  4600  			2,
  4601  		)
  4602  
  4603  		// m1 accepts <post 2, #1>
  4604  		_acceptNFTTransferWithTestMeta(
  4605  			testMeta,
  4606  			10,
  4607  			m1Pub,
  4608  			m1Priv,
  4609  			post2Hash,
  4610  			1,
  4611  		)
  4612  
  4613  		// Check the state of the accepted NFTs.
  4614  		acceptedNFT1 := DBGetNFTEntryByPostHashSerialNumber(db, post1Hash, 2)
  4615  		require.Equal(acceptedNFT1.IsPending, false)
  4616  
  4617  		acceptedNFT2 := DBGetNFTEntryByPostHashSerialNumber(db, post2Hash, 1)
  4618  		require.Equal(acceptedNFT2.IsPending, false)
  4619  	}
  4620  
  4621  	// Now let's test out burning NFTs.
  4622  
  4623  	// Error case: non-existent NFT.
  4624  	{
  4625  		_, _, _, err := _burnNFT(
  4626  			t, chain, db, params, 10,
  4627  			m0Pub,
  4628  			m0Priv,
  4629  			post1Hash,
  4630  			6, /*Non-existent serial number.*/
  4631  		)
  4632  
  4633  		require.Error(err)
  4634  		require.Contains(err.Error(), RuleErrorCannotBurnNonExistentNFT)
  4635  	}
  4636  
  4637  	// Error case: transfer by non-owner (m1 owns <post 2, #1>).
  4638  	{
  4639  		_, _, _, err := _burnNFT(
  4640  			t, chain, db, params, 10,
  4641  			m0Pub,
  4642  			m0Priv,
  4643  			post2Hash,
  4644  			1,
  4645  		)
  4646  
  4647  		require.Error(err)
  4648  		require.Contains(err.Error(), RuleErrorBurnNFTByNonOwner)
  4649  	}
  4650  
  4651  	// Error case: cannot burn an NFT that is for sale (<post 1, #1> is still for sale).
  4652  	{
  4653  		_, _, _, err := _burnNFT(
  4654  			t, chain, db, params, 10,
  4655  			m0Pub,
  4656  			m0Priv,
  4657  			post1Hash,
  4658  			1,
  4659  		)
  4660  
  4661  		require.Error(err)
  4662  		require.Contains(err.Error(), RuleErrorCannotBurnNFTThatIsForSale)
  4663  	}
  4664  
  4665  	// Let's burn some NFTs!!
  4666  	{
  4667  		// m3 burns <post 1, #5> (not for sale, is pending, no unlockable)
  4668  		_burnNFTWithTestMeta(
  4669  			testMeta,
  4670  			10,
  4671  			m3Pub,
  4672  			m3Priv,
  4673  			post1Hash,
  4674  			5,
  4675  		)
  4676  
  4677  		// m0 burns <post 2, #3> (not for sale, not pending, has unlockable)
  4678  		_burnNFTWithTestMeta(
  4679  			testMeta,
  4680  			10,
  4681  			m0Pub,
  4682  			m0Priv,
  4683  			post2Hash,
  4684  			3,
  4685  		)
  4686  
  4687  		// Check the burned NFTs no longer exist.
  4688  		burnedNFT1 := DBGetNFTEntryByPostHashSerialNumber(db, post1Hash, 5)
  4689  		require.Nil(burnedNFT1)
  4690  
  4691  		burnedNFT2 := DBGetNFTEntryByPostHashSerialNumber(db, post2Hash, 3)
  4692  		require.Nil(burnedNFT2)
  4693  
  4694  		// Check that the post entries have the correct burn count.
  4695  		post1 := DBGetPostEntryByPostHash(db, post1Hash)
  4696  		require.Equal(uint64(1), post1.NumNFTCopiesBurned)
  4697  
  4698  		post2 := DBGetPostEntryByPostHash(db, post2Hash)
  4699  		require.Equal(uint64(1), post2.NumNFTCopiesBurned)
  4700  	}
  4701  
  4702  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  4703  	_rollBackTestMetaTxnsAndFlush(testMeta)
  4704  	_applyTestMetaTxnsToMempool(testMeta)
  4705  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  4706  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  4707  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  4708  }
  4709  
  4710  func TestBidAmountZero(t *testing.T) {
  4711  
  4712  	assert := assert.New(t)
  4713  	require := require.New(t)
  4714  	_ = assert
  4715  	_ = require
  4716  
  4717  	chain, params, db := NewLowDifficultyBlockchain()
  4718  	mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/)
  4719  	// Make m3, m4 a paramUpdater for this test
  4720  	params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true
  4721  	params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true
  4722  
  4723  	// Mine a few blocks to give the senderPkString some money.
  4724  	_, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  4725  	require.NoError(err)
  4726  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  4727  	require.NoError(err)
  4728  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  4729  	require.NoError(err)
  4730  	_, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool)
  4731  	require.NoError(err)
  4732  
  4733  	// We build the testMeta obj after mining blocks so that we save the correct block height.
  4734  	testMeta := &TestMeta{
  4735  		t:           t,
  4736  		chain:       chain,
  4737  		params:      params,
  4738  		db:          db,
  4739  		mempool:     mempool,
  4740  		miner:       miner,
  4741  		savedHeight: chain.blockTip().Height + 1,
  4742  	}
  4743  
  4744  	// Fund all the keys.
  4745  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 1000)
  4746  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 1000)
  4747  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 1000)
  4748  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m3Pub, senderPrivString, 1000)
  4749  	_registerOrTransferWithTestMeta(testMeta, "", senderPkString, m4Pub, senderPrivString, 100)
  4750  
  4751  	// Set max copies to a non-zero value to activate NFTs.
  4752  	{
  4753  		_updateGlobalParamsEntryWithTestMeta(
  4754  			testMeta,
  4755  			10, /*FeeRateNanosPerKB*/
  4756  			m4Pub,
  4757  			m4Priv,
  4758  			-1, -1, -1, -1,
  4759  			1000, /*maxCopiesPerNFT*/
  4760  		)
  4761  	}
  4762  
  4763  	// Create a post for testing.
  4764  	{
  4765  		_submitPostWithTestMeta(
  4766  			testMeta,
  4767  			10,                                 /*feeRateNanosPerKB*/
  4768  			m0Pub,                              /*updaterPkBase58Check*/
  4769  			m0Priv,                             /*updaterPrivBase58Check*/
  4770  			[]byte{},                           /*postHashToModify*/
  4771  			[]byte{},                           /*parentStakeID*/
  4772  			&DeSoBodySchema{Body: "m0 post 1"}, /*body*/
  4773  			[]byte{},
  4774  			1502947011*1e9, /*tstampNanos*/
  4775  			false /*isHidden*/)
  4776  	}
  4777  	post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash()
  4778  
  4779  	// NFT the post.
  4780  	{
  4781  		// You need a profile in order to create an NFT.
  4782  		_updateProfileWithTestMeta(
  4783  			testMeta,
  4784  			10,            /*feeRateNanosPerKB*/
  4785  			m0Pub,         /*updaterPkBase58Check*/
  4786  			m0Priv,        /*updaterPrivBase58Check*/
  4787  			[]byte{},      /*profilePubKey*/
  4788  			"m2",          /*newUsername*/
  4789  			"i am the m2", /*newDescription*/
  4790  			shortPic,      /*newProfilePic*/
  4791  			10*100,        /*newCreatorBasisPoints*/
  4792  			1.25*100*100,  /*newStakeMultipleBasisPoints*/
  4793  			false /*isHidden*/)
  4794  
  4795  		// We only need 1 copy for this test.
  4796  		_createNFTWithTestMeta(
  4797  			testMeta,
  4798  			10, /*FeeRateNanosPerKB*/
  4799  			m0Pub,
  4800  			m0Priv,
  4801  			post1Hash,
  4802  			1,     /*NumCopies*/
  4803  			false, /*HasUnlockable*/
  4804  			true,  /*IsForSale*/
  4805  			0,     /*MinBidAmountNanos*/
  4806  			0,     /*nftFee*/
  4807  			0,     /*nftRoyaltyToCreatorBasisPoints*/
  4808  			0,     /*nftRoyaltyToCoinBasisPoints*/
  4809  		)
  4810  
  4811  		// Post 1 should have 1 copies.
  4812  		dbEntries := DBGetNFTEntriesForPostHash(db, post1Hash)
  4813  		require.Equal(1, len(dbEntries))
  4814  	}
  4815  
  4816  	// Case: User can submit a bid of amount 0 on an NFT with MinBidAmountNanos of 0. It doesn't do anything though.
  4817  	{
  4818  		// M1 places a pointless bid of 0.
  4819  		_createNFTBidWithTestMeta(
  4820  			testMeta,
  4821  			10, /*FeeRateNanosPerKB*/
  4822  			m1Pub,
  4823  			m1Priv,
  4824  			post1Hash,
  4825  			1, /*SerialNumber*/
  4826  			0, /*BidAmountNanos*/
  4827  		)
  4828  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  4829  		require.Equal(0, len(bidEntries))
  4830  
  4831  	}
  4832  	// Case: User submits bid and cancels it. Bid cannot be accepted. Users submits new bid. It can be accepted.
  4833  	// Have m1 place a bid and m0 accept it.
  4834  	{
  4835  		// Place a bid of 1 nano
  4836  		_createNFTBidWithTestMeta(
  4837  			testMeta,
  4838  			10, /*FeeRateNanosPerKB*/
  4839  			m1Pub,
  4840  			m1Priv,
  4841  			post1Hash,
  4842  			1, /*SerialNumber*/
  4843  			1, /*BidAmountNanos*/
  4844  		)
  4845  		bidEntries := DBGetNFTBidEntries(db, post1Hash, 1)
  4846  		require.Equal(1, len(bidEntries))
  4847  
  4848  		// Cancel bid.
  4849  		_createNFTBidWithTestMeta(
  4850  			testMeta,
  4851  			10, /*FeeRateNanosPerKB*/
  4852  			m1Pub,
  4853  			m1Priv,
  4854  			post1Hash,
  4855  			1, /*SerialNumber*/
  4856  			0, /*BidAmountNanos*/
  4857  		)
  4858  
  4859  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  4860  		require.Equal(0, len(bidEntries))
  4861  
  4862  		{
  4863  			_, _, _, err = _acceptNFTBid(
  4864  				t, chain, db, params, 10,
  4865  				m0Pub,
  4866  				m0Priv,
  4867  				post1Hash,
  4868  				1, /*SerialNumber*/
  4869  				m1Pub,
  4870  				1, /*BidAmountNanos*/
  4871  				"",  /*UnlockableText*/
  4872  			)
  4873  			require.Error(err)
  4874  			require.Contains(err.Error(), RuleErrorCantAcceptNonExistentBid)
  4875  		}
  4876  
  4877  		// Place a bid of 2 nanos
  4878  		_createNFTBidWithTestMeta(
  4879  			testMeta,
  4880  			10, /*FeeRateNanosPerKB*/
  4881  			m1Pub,
  4882  			m1Priv,
  4883  			post1Hash,
  4884  			1, /*SerialNumber*/
  4885  			2, /*BidAmountNanos*/
  4886  		)
  4887  
  4888  		// Accept that bid
  4889  		_acceptNFTBidWithTestMeta(
  4890  			testMeta,
  4891  			10, /*FeeRateNanosPerKB*/
  4892  			m0Pub,
  4893  			m0Priv,
  4894  			post1Hash,
  4895  			1, /*SerialNumber*/
  4896  			m1Pub,
  4897  			2,  /*BidAmountNanos*/
  4898  			"", /*UnlockableText*/
  4899  		)
  4900  
  4901  		// There are no bid entries after it has been accepted.
  4902  		bidEntries = DBGetNFTBidEntries(db, post1Hash, 1)
  4903  		require.Equal(0, len(bidEntries))
  4904  
  4905  		nftEntry := DBGetNFTEntryByPostHashSerialNumber(db, post1Hash, 1)
  4906  		m1PKID := DBGetPKIDEntryForPublicKey(db, m1PkBytes)
  4907  		require.Equal(nftEntry.OwnerPKID, m1PKID.PKID)
  4908  	}
  4909  
  4910  	// Roll all successful txns through connect and disconnect loops to make sure nothing breaks.
  4911  	_rollBackTestMetaTxnsAndFlush(testMeta)
  4912  	_applyTestMetaTxnsToMempool(testMeta)
  4913  	_applyTestMetaTxnsToViewAndFlush(testMeta)
  4914  	_disconnectTestMetaTxnsFromViewAndFlush(testMeta)
  4915  	_connectBlockThenDisconnectBlockAndFlush(testMeta)
  4916  
  4917  
  4918  }