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 }