github.com/deso-protocol/core@v1.2.9/lib/block_view_post_test.go (about) 1 package lib 2 3 import ( 4 "encoding/hex" 5 "encoding/json" 6 "fmt" 7 "github.com/dgraph-io/badger/v3" 8 "github.com/pkg/errors" 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 "reflect" 12 "sort" 13 "testing" 14 "time" 15 ) 16 17 func _submitPost(t *testing.T, chain *Blockchain, db *badger.DB, 18 params *DeSoParams, feeRateNanosPerKB uint64, updaterPkBase58Check string, 19 updaterPrivBase58Check string, postHashToModify []byte, 20 parentStakeID []byte, 21 bodyObj *DeSoBodySchema, 22 repostedPostHash []byte, 23 tstampNanos uint64, 24 isHidden bool) ( 25 _utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) { 26 27 assert := assert.New(t) 28 require := require.New(t) 29 _ = assert 30 _ = require 31 32 updaterPkBytes, _, err := Base58CheckDecode(updaterPkBase58Check) 33 require.NoError(err) 34 35 utxoView, err := NewUtxoView(db, params, nil) 36 require.NoError(err) 37 38 body, err := json.Marshal(bodyObj) 39 require.NoError(err) 40 41 isQuotedRepost := false 42 if len(repostedPostHash) > 0 && (bodyObj.Body != "" || len(bodyObj.ImageURLs) > 0 || len(bodyObj.VideoURLs) > 0) { 43 isQuotedRepost = true 44 } 45 postExtraData := make(map[string][]byte) 46 txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateSubmitPostTxn( 47 updaterPkBytes, 48 postHashToModify, 49 parentStakeID, 50 body, 51 repostedPostHash, 52 isQuotedRepost, 53 tstampNanos, 54 postExtraData, 55 isHidden, 56 feeRateNanosPerKB, 57 nil, 58 []*DeSoOutput{}) 59 if err != nil { 60 return nil, nil, 0, err 61 } 62 63 require.Equal(totalInputMake, changeAmountMake+feesMake) 64 65 // Sign the transaction now that its inputs are set up. 66 _signTxn(t, txn, updaterPrivBase58Check) 67 68 txHash := txn.Hash() 69 // Always use height+1 for validation since it's assumed the transaction will 70 // get mined into the next block. 71 blockHeight := chain.blockTip().Height + 1 72 utxoOps, totalInput, totalOutput, fees, err := 73 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 74 // ConnectTransaction should treat the amount locked as contributing to the 75 // output. 76 if err != nil { 77 return nil, nil, 0, err 78 } 79 require.Equal(totalInput, totalOutput+fees) 80 require.Equal(totalInput, totalInputMake) 81 82 // We should have one SPEND UtxoOperation for each input, one ADD operation 83 // for each output, and one OperationTypePrivateMessage operation at the end. 84 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 85 for ii := 0; ii < len(txn.TxInputs); ii++ { 86 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 87 } 88 require.Equal(OperationTypeSubmitPost, utxoOps[len(utxoOps)-1].Type) 89 90 require.NoError(utxoView.FlushToDb()) 91 92 return utxoOps, txn, blockHeight, nil 93 } 94 95 func _submitPostWithTestMeta( 96 testMeta *TestMeta, 97 feeRateNanosPerKB uint64, 98 updaterPkBase58Check string, 99 updaterPrivBase58Check string, 100 postHashToModify []byte, 101 parentStakeID []byte, 102 body *DeSoBodySchema, 103 repostedPostHash []byte, 104 tstampNanos uint64, 105 isHidden bool) { 106 107 testMeta.expectedSenderBalances = append( 108 testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, updaterPkBase58Check)) 109 110 currentOps, currentTxn, _, err := _submitPost( 111 testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB, 112 updaterPkBase58Check, 113 updaterPrivBase58Check, 114 postHashToModify, 115 parentStakeID, 116 body, 117 repostedPostHash, 118 tstampNanos, 119 isHidden) 120 121 require.NoError(testMeta.t, err) 122 123 testMeta.txnOps = append(testMeta.txnOps, currentOps) 124 testMeta.txns = append(testMeta.txns, currentTxn) 125 } 126 127 func _giveDeSoDiamonds(t *testing.T, chain *Blockchain, db *badger.DB, params *DeSoParams, 128 feeRateNanosPerKB uint64, senderPkBase58Check string, senderPrivBase58Check string, 129 diamondPostHash *BlockHash, diamondLevel int64, deleteDiamondLevel bool, 130 ) (_utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) { 131 132 senderPkBytes, _, err := Base58CheckDecode(senderPkBase58Check) 133 require.NoError(t, err) 134 135 utxoView, err := NewUtxoView(db, params, nil) 136 require.NoError(t, err) 137 138 txn, totalInputMake, spendAmount, changeAmountMake, feesMake, err := chain.CreateBasicTransferTxnWithDiamonds( 139 senderPkBytes, 140 diamondPostHash, 141 diamondLevel, 142 feeRateNanosPerKB, 143 nil, 144 []*DeSoOutput{}) 145 if err != nil { 146 return nil, nil, 0, err 147 } 148 149 require.Equal(t, totalInputMake, spendAmount+changeAmountMake+feesMake) 150 151 // For testing purposes. 152 if deleteDiamondLevel { 153 delete(txn.ExtraData, DiamondLevelKey) 154 } 155 156 // Sign the transaction now that its inputs are set up. 157 _signTxn(t, txn, senderPrivBase58Check) 158 159 txHash := txn.Hash() 160 // Always use height+1 for validation since it's assumed the transaction will 161 // get mined into the next block. 162 blockHeight := chain.blockTip().Height + 1 163 utxoOps, totalInput, totalOutput, fees, err := 164 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 165 if err != nil { 166 return nil, nil, 0, err 167 } 168 require.Equal(t, totalInput, totalOutput+fees) 169 require.Equal(t, totalInput, totalInputMake) 170 171 // We should have one SPEND UtxoOperation for each input, one ADD operation 172 // for each output, and one OperationTypeDeSoDiamond operation at the end. 173 require.Equal(t, len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 174 for ii := 0; ii < len(txn.TxInputs); ii++ { 175 require.Equal(t, OperationTypeSpendUtxo, utxoOps[ii].Type) 176 } 177 require.Equal(t, OperationTypeDeSoDiamond, utxoOps[len(utxoOps)-1].Type) 178 179 require.NoError(t, utxoView.FlushToDb()) 180 181 return utxoOps, txn, blockHeight, nil 182 } 183 184 func _giveDeSoDiamondsWithTestMeta( 185 testMeta *TestMeta, 186 feeRateNanosPerKB uint64, 187 senderPkBase58Check string, 188 senderPrivBase58Check string, 189 postHashToModify *BlockHash, 190 diamondLevel int64, 191 ) { 192 193 testMeta.expectedSenderBalances = append( 194 testMeta.expectedSenderBalances, _getBalance(testMeta.t, testMeta.chain, nil, senderPkBase58Check)) 195 currentOps, currentTxn, _, err := _giveDeSoDiamonds( 196 testMeta.t, testMeta.chain, testMeta.db, testMeta.params, feeRateNanosPerKB, 197 senderPkBase58Check, 198 senderPrivBase58Check, 199 postHashToModify, 200 diamondLevel, 201 false, 202 ) 203 require.NoError(testMeta.t, err) 204 205 testMeta.txnOps = append(testMeta.txnOps, currentOps) 206 testMeta.txns = append(testMeta.txns, currentTxn) 207 } 208 209 func _doSubmitPostTxn(t *testing.T, chain *Blockchain, db *badger.DB, 210 params *DeSoParams, feeRateNanosPerKB uint64, 211 UpdaterPublicKeyBase58Check string, UpdaterPrivateKeyBase58Check string, 212 postHashToModify []byte, 213 parentPostHashBytes []byte, 214 body string, 215 extraData map[string][]byte, 216 isHidden bool) ( 217 _utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) { 218 219 assert := assert.New(t) 220 require := require.New(t) 221 _ = assert 222 _ = require 223 224 updaterPkBytes, _, err := Base58CheckDecode(UpdaterPublicKeyBase58Check) 225 require.NoError(err) 226 227 utxoView, err := NewUtxoView(db, params, nil) 228 require.NoError(err) 229 230 txn, totalInputMake, _, _, err := chain.CreateSubmitPostTxn( 231 updaterPkBytes, 232 postHashToModify, 233 parentPostHashBytes, 234 []byte(body), 235 nil, 236 false, 237 uint64(time.Now().UnixNano()), 238 extraData, 239 isHidden, 240 feeRateNanosPerKB, 241 nil, /*mempool*/ 242 []*DeSoOutput{}) 243 if err != nil { 244 return nil, nil, 0, err 245 } 246 247 // Sign the transaction now that its inputs are set up. 248 _signTxn(t, txn, UpdaterPrivateKeyBase58Check) 249 250 txHash := txn.Hash() 251 // Always use height+1 for validation since it's assumed the transaction will 252 // get mined into the next block. 253 blockHeight := chain.blockTip().Height + 1 254 utxoOps, totalInput, totalOutput, fees, err := 255 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 256 // ConnectTransaction should treat the amount locked as contributing to the 257 // output. 258 if err != nil { 259 return nil, nil, 0, err 260 } 261 require.Equal(totalInput, totalOutput+fees) 262 require.GreaterOrEqual(totalInput, totalInputMake) 263 264 // We should have one SPEND UtxoOperation for each input, one ADD operation 265 // for each output, and one OperationTypeSubmitPost operation at the end. 266 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 267 for ii := 0; ii < len(txn.TxInputs); ii++ { 268 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 269 } 270 require.Equal(OperationTypeSubmitPost, utxoOps[len(utxoOps)-1].Type) 271 272 require.NoError(utxoView.FlushToDb()) 273 274 return utxoOps, txn, blockHeight, nil 275 } 276 277 func TestSubmitPost(t *testing.T) { 278 assert := assert.New(t) 279 require := require.New(t) 280 _ = assert 281 _ = require 282 283 chain, params, db := NewLowDifficultyBlockchain() 284 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 285 // Make m3 a paramUpdater for this test 286 params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true 287 288 // Mine a few blocks to give the senderPkString some money. 289 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 290 require.NoError(err) 291 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 292 require.NoError(err) 293 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 294 require.NoError(err) 295 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 296 require.NoError(err) 297 298 // Setup some convenience functions for the test. 299 txnOps := [][]*UtxoOperation{} 300 txns := []*MsgDeSoTxn{} 301 expectedSenderBalances := []uint64{} 302 303 // We take the block tip to be the blockchain height rather than the 304 // header chain height. 305 savedHeight := chain.blockTip().Height + 1 306 registerOrTransfer := func(username string, 307 senderPk string, recipientPk string, senderPriv string) { 308 309 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, senderPk)) 310 311 currentOps, currentTxn, _ := _doBasicTransferWithViewFlush( 312 t, chain, db, params, senderPk, recipientPk, 313 senderPriv, 70 /*amount to send*/, 11 /*feerate*/) 314 315 txnOps = append(txnOps, currentOps) 316 txns = append(txns, currentTxn) 317 } 318 319 // Fund all the keys. 320 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 321 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 322 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 323 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 324 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 325 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 326 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 327 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 328 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 329 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 330 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 331 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 332 333 checkPostsDeleted := func() { 334 utxoView, err := NewUtxoView(db, params, nil) 335 require.NoError(err) 336 corePosts, commentsByPostHash, err := utxoView.GetAllPosts() 337 require.NoError(err) 338 require.Equal(4, len(corePosts)) 339 totalComments := 0 340 for _, currentComment := range commentsByPostHash { 341 totalComments += len(currentComment) 342 } 343 // 3 comments from seed txns 344 require.Equal(3, totalComments) 345 346 require.Equal(0, len(utxoView.RepostKeyToRepostEntry)) 347 348 // TODO: add checks that repost entries are deleted 349 } 350 checkPostsDeleted() 351 352 updateProfile := func( 353 feeRateNanosPerKB uint64, updaterPkBase58Check string, 354 updaterPrivBase58Check string, profilePubKey []byte, newUsername string, 355 newDescription string, newProfilePic string, newCreatorBasisPoints uint64, 356 newStakeMultipleBasisPoints uint64, isHidden bool) { 357 358 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, updaterPkBase58Check)) 359 360 currentOps, currentTxn, _, err := _updateProfile( 361 t, chain, db, params, 362 feeRateNanosPerKB, updaterPkBase58Check, 363 updaterPrivBase58Check, profilePubKey, newUsername, 364 newDescription, newProfilePic, newCreatorBasisPoints, 365 newStakeMultipleBasisPoints, isHidden) 366 367 require.NoError(err) 368 369 txnOps = append(txnOps, currentOps) 370 txns = append(txns, currentTxn) 371 } 372 _, _, _ = m2Priv, m3Priv, updateProfile 373 374 submitPost := func( 375 feeRateNanosPerKB uint64, updaterPkBase58Check string, 376 updaterPrivBase58Check string, 377 postHashToModify []byte, 378 parentStakeID []byte, 379 body *DeSoBodySchema, 380 repostedPostHash []byte, 381 tstampNanos uint64, 382 isHidden bool) { 383 384 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, updaterPkBase58Check)) 385 386 currentOps, currentTxn, _, err := _submitPost( 387 t, chain, db, params, feeRateNanosPerKB, 388 updaterPkBase58Check, 389 updaterPrivBase58Check, 390 postHashToModify, 391 parentStakeID, 392 body, 393 repostedPostHash, 394 tstampNanos, 395 isHidden) 396 397 require.NoError(err) 398 399 txnOps = append(txnOps, currentOps) 400 txns = append(txns, currentTxn) 401 } 402 _ = submitPost 403 404 swapIdentity := func( 405 feeRateNanosPerKB uint64, updaterPkBase58Check string, 406 updaterPrivBase58Check string, 407 fromPkBytes []byte, 408 toPkBytes []byte) { 409 410 expectedSenderBalances = append(expectedSenderBalances, _getBalance(t, chain, nil, updaterPkBase58Check)) 411 412 currentOps, currentTxn, _, err := _swapIdentity( 413 t, chain, db, params, feeRateNanosPerKB, 414 updaterPkBase58Check, 415 updaterPrivBase58Check, 416 fromPkBytes, toPkBytes) 417 418 require.NoError(err) 419 420 txnOps = append(txnOps, currentOps) 421 txns = append(txns, currentTxn) 422 } 423 424 // Creating a post from an unregistered profile should succeed 425 { 426 submitPost( 427 10, /*feeRateNanosPerKB*/ 428 m0Pub, /*updaterPkBase58Check*/ 429 m0Priv, /*updaterPrivBase58Check*/ 430 []byte{}, /*postHashToModify*/ 431 []byte{}, /*parentStakeID*/ 432 &DeSoBodySchema{Body: "m0 post body 1 no profile"}, /*body*/ 433 []byte{}, 434 1502947011*1e9, /*tstampNanos*/ 435 false /*isHidden*/) 436 } 437 post1Txn := txns[len(txns)-1] 438 post1Hash := post1Txn.Hash() 439 _, _ = post1Txn, post1Hash 440 441 { 442 submitPost( 443 10, /*feeRateNanosPerKB*/ 444 m0Pub, /*updaterPkBase58Check*/ 445 m0Priv, /*updaterPrivBase58Check*/ 446 []byte{}, /*postHashToModify*/ 447 []byte{}, /*parentStakeID*/ 448 &DeSoBodySchema{Body: "m0 post body 2 no profile"}, /*body*/ 449 []byte{}, 450 1502947012*1e9, /*tstampNanos*/ 451 false /*isHidden*/) 452 } 453 post2Txn := txns[len(txns)-1] 454 post2Hash := post2Txn.Hash() 455 _, _ = post2Txn, post2Hash 456 457 { 458 submitPost( 459 10, /*feeRateNanosPerKB*/ 460 m1Pub, /*updaterPkBase58Check*/ 461 m1Priv, /*updaterPrivBase58Check*/ 462 []byte{}, /*postHashToModify*/ 463 []byte{}, /*parentStakeID*/ 464 &DeSoBodySchema{Body: "m1 post body 1 no profile"}, /*body*/ 465 []byte{}, 466 1502947013*1e9, /*tstampNanos*/ 467 false /*isHidden*/) 468 } 469 post3Txn := txns[len(txns)-1] 470 post3Hash := post3Txn.Hash() 471 _, _ = post3Txn, post3Hash 472 473 // Creating a post from a registered profile should succeed 474 { 475 updateProfile( 476 1, /*feeRateNanosPerKB*/ 477 m2Pub, /*updaterPkBase58Check*/ 478 m2Priv, /*updaterPrivBase58Check*/ 479 []byte{}, /*profilePubKey*/ 480 "m2", /*newUsername*/ 481 "i am the m2", /*newDescription*/ 482 shortPic, /*newProfilePic*/ 483 10*100, /*newCreatorBasisPoints*/ 484 1.25*100*100, /*newStakeMultipleBasisPoints*/ 485 false /*isHidden*/) 486 487 submitPost( 488 10, /*feeRateNanosPerKB*/ 489 m2Pub, /*updaterPkBase58Check*/ 490 m2Priv, /*updaterPrivBase58Check*/ 491 []byte{}, /*postHashToModify*/ 492 []byte{}, /*parentStakeID*/ 493 &DeSoBodySchema{Body: "m2 post body 1 WITH profile"}, /*body*/ 494 []byte{}, 495 1502947014*1e9, /*tstampNanos*/ 496 false /*isHidden*/) 497 } 498 post4Txn := txns[len(txns)-1] 499 post4Hash := post4Txn.Hash() 500 _, _ = post4Txn, post4Hash 501 502 { 503 updateProfile( 504 1, /*feeRateNanosPerKB*/ 505 m3Pub, /*updaterPkBase58Check*/ 506 m3Priv, /*updaterPrivBase58Check*/ 507 []byte{}, /*profilePubKey*/ 508 "m3", /*newUsername*/ 509 "i am the m3", /*newDescription*/ 510 shortPic, /*newProfilePic*/ 511 10*100, /*newCreatorBasisPoints*/ 512 1.25*100*100, /*newStakeMultipleBasisPoints*/ 513 false /*isHidden*/) 514 515 submitPost( 516 10, /*feeRateNanosPerKB*/ 517 m3Pub, /*updaterPkBase58Check*/ 518 m3Priv, /*updaterPrivBase58Check*/ 519 []byte{}, /*postHashToModify*/ 520 []byte{}, /*parentStakeID*/ 521 &DeSoBodySchema{Body: "m3 post body 1 WITH profile"}, /*body*/ 522 []byte{}, 523 1502947015*1e9, /*tstampNanos*/ 524 false /*isHidden*/) 525 } 526 post5Txn := txns[len(txns)-1] 527 post5Hash := post5Txn.Hash() 528 _, _ = post5Txn, post5Hash 529 530 // Create another post for m2 531 { 532 submitPost( 533 10, /*feeRateNanosPerKB*/ 534 m2Pub, /*updaterPkBase58Check*/ 535 m2Priv, /*updaterPrivBase58Check*/ 536 []byte{}, /*postHashToModify*/ 537 []byte{}, /*parentStakeID*/ 538 &DeSoBodySchema{Body: "m2 post body 2 WITH profile"}, /*body*/ 539 []byte{}, 540 1502947016*1e9, /*tstampNanos*/ 541 false /*isHidden*/) 542 } 543 post6Txn := txns[len(txns)-1] 544 post6Hash := post6Txn.Hash() 545 _, _ = post6Txn, post6Hash 546 547 // A zero input post should fail 548 { 549 _, _, _, err := _submitPost( 550 t, chain, db, params, 551 0, /*feeRateNanosPerKB*/ 552 m0Pub, /*updaterPkBase58Check*/ 553 m0Priv, /*updaterPrivBase58Check*/ 554 []byte{}, /*postHashToModify*/ 555 []byte{}, /*parentStakeID*/ 556 &DeSoBodySchema{Body: "this is a post body"}, /*body*/ 557 []byte{}, 558 1502947011*1e9, /*tstampNanos*/ 559 false /*isHidden*/) 560 require.Error(err) 561 require.Contains(err.Error(), RuleErrorTxnMustHaveAtLeastOneInput) 562 } 563 564 // PostHashToModify with bad length 565 { 566 _, _, _, err := _submitPost( 567 t, chain, db, params, 568 10, /*feeRateNanosPerKB*/ 569 m0Pub, /*updaterPkBase58Check*/ 570 m0Priv, /*updaterPrivBase58Check*/ 571 RandomBytes(HashSizeBytes-1), /*postHashToModify*/ 572 []byte{}, /*parentStakeID*/ 573 &DeSoBodySchema{Body: "this is a post body"}, /*body*/ 574 []byte{}, 575 1502947048*1e9, /*tstampNanos*/ 576 false /*isHidden*/) 577 require.Error(err) 578 require.Contains(err.Error(), RuleErrorSubmitPostInvalidPostHashToModify) 579 } 580 581 // Setting PostHashToModify should fail for a non-existent post 582 { 583 _, _, _, err := _submitPost( 584 t, chain, db, params, 585 10, /*feeRateNanosPerKB*/ 586 m0Pub, /*updaterPkBase58Check*/ 587 m0Priv, /*updaterPrivBase58Check*/ 588 RandomBytes(HashSizeBytes), /*postHashToModify*/ 589 []byte{}, /*parentStakeID*/ 590 &DeSoBodySchema{Body: "this is a post body"}, /*body*/ 591 []byte{}, 592 1502947048*1e9, /*tstampNanos*/ 593 false /*isHidden*/) 594 require.Error(err) 595 require.Contains(err.Error(), RuleErrorSubmitPostModifyingNonexistentPost) 596 } 597 598 // Bad length for parent stake id should fail 599 { 600 _, _, _, err := _submitPost( 601 t, chain, db, params, 602 10, /*feeRateNanosPerKB*/ 603 m0Pub, /*updaterPkBase58Check*/ 604 m0Priv, /*updaterPrivBase58Check*/ 605 []byte{}, /*postHashToModify*/ 606 RandomBytes(HashSizeBytes-1), /*parentStakeID*/ 607 &DeSoBodySchema{Body: "this is a post body"}, /*body*/ 608 []byte{}, 609 1502947048*1e9, /*tstampNanos*/ 610 false /*isHidden*/) 611 require.Error(err) 612 require.Contains(err.Error(), RuleErrorSubmitPostInvalidParentStakeIDLength) 613 } 614 615 // Non-owner modifying post should fail 616 { 617 _, _, _, err := _submitPost( 618 t, chain, db, params, 619 10, /*feeRateNanosPerKB*/ 620 m1Pub, /*updaterPkBase58Check*/ 621 m1Priv, /*updaterPrivBase58Check*/ 622 post1Hash[:], /*postHashToModify*/ 623 []byte{}, /*parentStakeID*/ 624 &DeSoBodySchema{Body: "this is a post body"}, /*body*/ 625 []byte{}, 626 1502947048*1e9, /*tstampNanos*/ 627 false /*isHidden*/) 628 require.Error(err) 629 require.Contains(err.Error(), RuleErrorSubmitPostPostModificationNotAuthorized) 630 } 631 632 // Zero timestamp should fail 633 { 634 _, _, _, err = _submitPost( 635 t, chain, db, params, 636 10, /*feeRateNanosPerKB*/ 637 m1Pub, /*updaterPkBase58Check*/ 638 m1Priv, /*updaterPrivBase58Check*/ 639 []byte{}, /*postHashToModify*/ 640 []byte{}, /*parentStakeID*/ 641 &DeSoBodySchema{Body: "this is a post body"}, /*body*/ 642 []byte{}, 643 0, /*tstampNanos*/ 644 false /*isHidden*/) 645 require.Error(err) 646 require.Contains(err.Error(), RuleErrorSubmitPostTimestampIsZero) 647 } 648 649 // User without profile modifying another user without profile's post 650 // should fail 651 { 652 _, _, _, err = _submitPost( 653 t, chain, db, params, 654 10, /*feeRateNanosPerKB*/ 655 m0Pub, /*updaterPkBase58Check*/ 656 m0Priv, /*updaterPrivBase58Check*/ 657 // this belongs to m1 who doesn't have a profile. 658 post3Hash[:], /*postHashToModify*/ 659 RandomBytes(HashSizeBytes), /*parentStakeID*/ 660 &DeSoBodySchema{Body: "m0 post body 2"}, /*body*/ 661 []byte{}, 662 1502947049*1e9, /*tstampNanos*/ 663 false /*isHidden*/) 664 require.Error(err) 665 require.Contains(err.Error(), RuleErrorSubmitPostPostModificationNotAuthorized) 666 } 667 668 // User WITH profile modifying another user without profile's post 669 // should fail 670 { 671 _, _, _, err = _submitPost( 672 t, chain, db, params, 673 10, /*feeRateNanosPerKB*/ 674 m2Pub, /*updaterPkBase58Check*/ 675 m2Priv, /*updaterPrivBase58Check*/ 676 // this belongs to m1 who doesn't have a profile. 677 post1Hash[:], /*postHashToModify*/ 678 RandomBytes(HashSizeBytes), /*parentStakeID*/ 679 &DeSoBodySchema{Body: "m0 post body 2"}, /*body*/ 680 []byte{}, 681 1502947049*1e9, /*tstampNanos*/ 682 false /*isHidden*/) 683 require.Error(err) 684 require.Contains(err.Error(), RuleErrorSubmitPostPostModificationNotAuthorized) 685 } 686 687 // User without profile modifying another user WITH profile's post 688 // should fail 689 { 690 _, _, _, err = _submitPost( 691 t, chain, db, params, 692 10, /*feeRateNanosPerKB*/ 693 m0Pub, /*updaterPkBase58Check*/ 694 m0Priv, /*updaterPrivBase58Check*/ 695 // this belongs to m1 who doesn't have a profile. 696 post4Hash[:], /*postHashToModify*/ 697 RandomBytes(HashSizeBytes), /*parentStakeID*/ 698 &DeSoBodySchema{Body: "m0 post body 2"}, /*body*/ 699 []byte{}, 700 1502947049*1e9, /*tstampNanos*/ 701 false /*isHidden*/) 702 require.Error(err) 703 require.Contains(err.Error(), RuleErrorSubmitPostPostModificationNotAuthorized) 704 } 705 706 // User WITH profile modifying another user WITH profile's post 707 // should fail 708 { 709 _, _, _, err = _submitPost( 710 t, chain, db, params, 711 10, /*feeRateNanosPerKB*/ 712 m2Pub, /*updaterPkBase58Check*/ 713 m2Priv, /*updaterPrivBase58Check*/ 714 // this belongs to m1 who doesn't have a profile. 715 post5Hash[:], /*postHashToModify*/ 716 RandomBytes(HashSizeBytes), /*parentStakeID*/ 717 &DeSoBodySchema{Body: "m0 post body 2"}, /*body*/ 718 []byte{}, 719 1502947049*1e9, /*tstampNanos*/ 720 false /*isHidden*/) 721 require.Error(err) 722 require.Contains(err.Error(), RuleErrorSubmitPostPostModificationNotAuthorized) 723 } 724 725 // Owner without profile modifying post should succeed but all the non-body fields 726 // should be ignored. 727 { 728 submitPost( 729 10, /*feeRateNanosPerKB*/ 730 m0Pub, /*updaterPkBase58Check*/ 731 m0Priv, /*updaterPrivBase58Check*/ 732 post1Hash[:], /*postHashToModify*/ 733 RandomBytes(HashSizeBytes), /*parentStakeID*/ 734 &DeSoBodySchema{Body: "m0 post body MODIFIED"}, /*body*/ 735 []byte{}, 736 1502947017*1e9, /*tstampNanos*/ 737 true /*isHidden*/) 738 } 739 740 // Owner with profile modifying one of their posts should succeed but 741 // all non-body posts should be unchanged. 742 { 743 submitPost( 744 10, /*feeRateNanosPerKB*/ 745 m2Pub, /*updaterPkBase58Check*/ 746 m2Priv, /*updaterPrivBase58Check*/ 747 post4Hash[:], /*postHashToModify*/ 748 RandomBytes(HashSizeBytes), /*parentStakeID*/ 749 &DeSoBodySchema{Body: "m2 post body MODIFIED"}, /*body*/ 750 []byte{}, 751 1502947018*1e9, /*tstampNanos*/ 752 true /*isHidden*/) 753 } 754 755 // ParamUpdater modifying their own post should succeed 756 { 757 submitPost( 758 10, /*feeRateNanosPerKB*/ 759 m3Pub, /*updaterPkBase58Check*/ 760 m3Priv, /*updaterPrivBase58Check*/ 761 post5Hash[:], /*postHashToModify*/ 762 RandomBytes(HashSizeBytes), /*parentStakeID*/ 763 &DeSoBodySchema{Body: "paramUpdater post body MODIFIED"}, /*body*/ 764 []byte{}, 765 1502947019*1e9, /*tstampNanos*/ 766 true /*isHidden*/) 767 } 768 769 // Modifying a post and then modifying it back should work. 770 { 771 submitPost( 772 10, /*feeRateNanosPerKB*/ 773 m1Pub, /*updaterPkBase58Check*/ 774 m1Priv, /*updaterPrivBase58Check*/ 775 post3Hash[:], /*postHashToModify*/ 776 RandomBytes(HashSizeBytes), /*parentStakeID*/ 777 &DeSoBodySchema{Body: "sldkfjlskdfjlajflkasjdflkasjdf"}, /*body*/ 778 []byte{}, 779 1502947022*1e9, /*tstampNanos*/ 780 true /*isHidden*/) 781 submitPost( 782 10, /*feeRateNanosPerKB*/ 783 m1Pub, /*updaterPkBase58Check*/ 784 m1Priv, /*updaterPrivBase58Check*/ 785 post3Hash[:], /*postHashToModify*/ 786 RandomBytes(HashSizeBytes), /*parentStakeID*/ 787 &DeSoBodySchema{Body: "m1 post body 1 no profile modified back"}, /*body*/ 788 []byte{}, 789 1502947049*1e9, /*tstampNanos*/ 790 false /*isHidden*/) 791 } 792 793 // Comment on a post with an anonymous public key 794 { 795 submitPost( 796 10, /*feeRateNanosPerKB*/ 797 m0Pub, /*updaterPkBase58Check*/ 798 m0Priv, /*updaterPrivBase58Check*/ 799 []byte{}, /*postHashToModify*/ 800 post3Hash[:], /*parentStakeID*/ 801 &DeSoBodySchema{Body: "comment 1 from m0 on post3"}, /*body*/ 802 []byte{}, 803 1502947001*1e9, /*tstampNanos*/ 804 false /*isHidden*/) 805 } 806 comment1Txn := txns[len(txns)-1] 807 comment1Hash := comment1Txn.Hash() 808 809 // Make a few more comments. 810 { 811 submitPost( 812 10, /*feeRateNanosPerKB*/ 813 m0Pub, /*updaterPkBase58Check*/ 814 m0Priv, /*updaterPrivBase58Check*/ 815 []byte{}, /*postHashToModify*/ 816 post6Hash[:], /*parentStakeID*/ 817 &DeSoBodySchema{Body: "comment 2 from m0 on post3"}, /*body*/ 818 []byte{}, 819 1502947002*1e9, /*tstampNanos*/ 820 false /*isHidden*/) 821 } 822 comment2CreatedTxnIndex := len(txns) - 1 823 comment2Txn := txns[comment2CreatedTxnIndex] 824 comment2Hash := comment2Txn.Hash() 825 826 { 827 submitPost( 828 10, /*feeRateNanosPerKB*/ 829 m2Pub, /*updaterPkBase58Check*/ 830 m2Priv, /*updaterPrivBase58Check*/ 831 []byte{}, /*postHashToModify*/ 832 post6Hash[:], /*parentStakeID*/ 833 &DeSoBodySchema{Body: "comment 1 from m2 on post6"}, /*body*/ 834 []byte{}, 835 1502947003*1e9, /*tstampNanos*/ 836 false /*isHidden*/) 837 } 838 comment3CreatedTxnIndex := len(txns) - 1 839 comment3Txn := txns[comment3CreatedTxnIndex] 840 comment3Hash := comment3Txn.Hash() 841 842 { 843 submitPost( 844 10, /*feeRateNanosPerKB*/ 845 m3Pub, /*updaterPkBase58Check*/ 846 m3Priv, /*updaterPrivBase58Check*/ 847 []byte{}, /*postHashToModify*/ 848 post6Hash[:], /*parentStakeID*/ 849 &DeSoBodySchema{Body: "comment 1 from m3 on post6"}, /*body*/ 850 []byte{}, 851 1502947004*1e9, /*tstampNanos*/ 852 false /*isHidden*/) 853 } 854 comment4CreatedTxnIndex := len(txns) - 1 855 comment4Txn := txns[comment4CreatedTxnIndex] 856 comment4Hash := comment4Txn.Hash() 857 858 // Modify some comments 859 var comment3HiddenTxnIndex int 860 { 861 // Modifying the comment with the wrong pub should fail. 862 _, _, _, err = _submitPost( 863 t, chain, db, params, 864 10, /*feeRateNanosPerKB*/ 865 m1Pub, /*updaterPkBase58Check*/ 866 m1Priv, /*updaterPrivBase58Check*/ 867 comment1Hash[:], /*postHashToModify*/ 868 []byte{}, /*parentStakeID*/ 869 &DeSoBodySchema{Body: "modifying comment 1 by m1 should fail"}, /*body*/ 870 []byte{}, 871 1502947049*1e9, /*tstampNanos*/ 872 false /*isHidden*/) 873 require.Error(err) 874 require.Contains(err.Error(), RuleErrorSubmitPostPostModificationNotAuthorized) 875 876 // Modifying the comment with the proper key should work. 877 submitPost( 878 10, /*feeRateNanosPerKB*/ 879 m0Pub, /*updaterPkBase58Check*/ 880 m0Priv, /*updaterPrivBase58Check*/ 881 comment1Hash[:], /*postHashToModify*/ 882 []byte{}, /*parentStakeID*/ 883 &DeSoBodySchema{Body: "comment from m0 on post3 MODIFIED"}, /*body*/ 884 []byte{}, 885 1502947049*1e9, /*tstampNanos*/ 886 false /*isHidden*/) 887 888 // Modifying the comment with the proper key should work. 889 submitPost( 890 10, /*feeRateNanosPerKB*/ 891 m2Pub, /*updaterPkBase58Check*/ 892 m2Priv, /*updaterPrivBase58Check*/ 893 comment3Hash[:], /*postHashToModify*/ 894 []byte{}, /*parentStakeID*/ 895 &DeSoBodySchema{Body: "comment from m2 on post6 MODIFIED"}, /*body*/ 896 []byte{}, 897 1502947049*1e9, /*tstampNanos*/ 898 true /*isHidden*/) 899 comment3HiddenTxnIndex = len(txns) - 1 900 901 // Modify a comment and modify it back. 902 submitPost( 903 10, /*feeRateNanosPerKB*/ 904 m0Pub, /*updaterPkBase58Check*/ 905 m0Priv, /*updaterPrivBase58Check*/ 906 comment2Hash[:], /*postHashToModify*/ 907 []byte{}, /*parentStakeID*/ 908 &DeSoBodySchema{Body: "comment from m0 on post3 MODIFIED"}, /*body*/ 909 []byte{}, 910 1502947049*1e9, /*tstampNanos*/ 911 true /*isHidden*/) 912 submitPost( 913 10, /*feeRateNanosPerKB*/ 914 m0Pub, /*updaterPkBase58Check*/ 915 m0Priv, /*updaterPrivBase58Check*/ 916 comment2Hash[:], /*postHashToModify*/ 917 []byte{}, /*parentStakeID*/ 918 &DeSoBodySchema{Body: "comment 2 from m0 on post3"}, /*body*/ 919 []byte{}, 920 1502947049*1e9, /*tstampNanos*/ 921 false /*isHidden*/) 922 } 923 924 // Commenting on a public key should work regardless of whether 925 // a profile actually exists for that stake ID. 926 { 927 submitPost( 928 10, /*feeRateNanosPerKB*/ 929 m0Pub, /*updaterPkBase58Check*/ 930 m0Priv, /*updaterPrivBase58Check*/ 931 []byte{}, /*postHashToModify*/ 932 m1PkBytes, /*parentStakeID*/ 933 &DeSoBodySchema{Body: "comment m0 on profile m1 [1]"}, /*body*/ 934 []byte{}, 935 1502947005*1e9, /*tstampNanos*/ 936 false /*isHidden*/) 937 } 938 comment5Txn := txns[len(txns)-1] 939 comment5Hash := comment5Txn.Hash() 940 { 941 submitPost( 942 10, /*feeRateNanosPerKB*/ 943 m1Pub, /*updaterPkBase58Check*/ 944 m1Priv, /*updaterPrivBase58Check*/ 945 []byte{}, /*postHashToModify*/ 946 m2PkBytes, /*parentStakeID*/ 947 &DeSoBodySchema{Body: "comment m1 on profile m2 [1]"}, /*body*/ 948 []byte{}, 949 1502947006*1e9, /*tstampNanos*/ 950 false /*isHidden*/) 951 } 952 comment6Txn := txns[len(txns)-1] 953 comment6Hash := comment6Txn.Hash() 954 { 955 submitPost( 956 10, /*feeRateNanosPerKB*/ 957 m3Pub, /*updaterPkBase58Check*/ 958 m3Priv, /*updaterPrivBase58Check*/ 959 []byte{}, /*postHashToModify*/ 960 m3PkBytes, /*parentStakeID*/ 961 &DeSoBodySchema{Body: "comment m3 on profile m3 [1]"}, /*body*/ 962 []byte{}, 963 1502947007*1e9, /*tstampNanos*/ 964 false /*isHidden*/) 965 } 966 comment7Txn := txns[len(txns)-1] 967 comment7Hash := comment7Txn.Hash() 968 { 969 submitPost( 970 10, /*feeRateNanosPerKB*/ 971 m0Pub, /*updaterPkBase58Check*/ 972 m0Priv, /*updaterPrivBase58Check*/ 973 []byte{}, /*postHashToModify*/ 974 m3PkBytes, /*parentStakeID*/ 975 &DeSoBodySchema{Body: "comment m0 on profile m3 [2]"}, /*body*/ 976 []byte{}, 977 1502947008*1e9, /*tstampNanos*/ 978 false /*isHidden*/) 979 } 980 comment8Txn := txns[len(txns)-1] 981 comment8Hash := comment8Txn.Hash() 982 983 // Modifying the profile comments should work when the key is authorized 984 // and fail when it's not. 985 { 986 submitPost( 987 10, /*feeRateNanosPerKB*/ 988 m0Pub, /*updaterPkBase58Check*/ 989 m0Priv, /*updaterPrivBase58Check*/ 990 comment5Hash[:], /*postHashToModify*/ 991 []byte{}, /*parentStakeID*/ 992 &DeSoBodySchema{Body: "comment 1 from m0 on post3 MODIFIED"}, /*body*/ 993 []byte{}, 994 1502947049*1e9, /*tstampNanos*/ 995 true /*isHidden*/) 996 997 _, _, _, err = _submitPost( 998 t, chain, db, params, 999 10, /*feeRateNanosPerKB*/ 1000 m1Pub, /*updaterPkBase58Check*/ 1001 m1Priv, /*updaterPrivBase58Check*/ 1002 comment5Hash[:], /*postHashToModify*/ 1003 []byte{}, /*parentStakeID*/ 1004 &DeSoBodySchema{Body: "modifying comment 1 by m1 should fail"}, /*body*/ 1005 []byte{}, 1006 1502947049*1e9, /*tstampNanos*/ 1007 false /*isHidden*/) 1008 require.Error(err) 1009 require.Contains(err.Error(), RuleErrorSubmitPostPostModificationNotAuthorized) 1010 1011 // Modify a profile comment then modify it back. 1012 submitPost( 1013 10, /*feeRateNanosPerKB*/ 1014 m1Pub, /*updaterPkBase58Check*/ 1015 m1Priv, /*updaterPrivBase58Check*/ 1016 comment6Hash[:], /*postHashToModify*/ 1017 []byte{}, /*parentStakeID*/ 1018 &DeSoBodySchema{Body: "comment m1 on profile m2 [1] MODIFIED"}, /*body*/ 1019 []byte{}, 1020 1502947049*1e9, /*tstampNanos*/ 1021 true /*isHidden*/) 1022 submitPost( 1023 10, /*feeRateNanosPerKB*/ 1024 m1Pub, /*updaterPkBase58Check*/ 1025 m1Priv, /*updaterPrivBase58Check*/ 1026 comment6Hash[:], /*postHashToModify*/ 1027 []byte{}, /*parentStakeID*/ 1028 &DeSoBodySchema{Body: "comment m1 on profile m2 [1]"}, /*body*/ 1029 []byte{}, 1030 1502947049*1e9, /*tstampNanos*/ 1031 false /*isHidden*/) 1032 } 1033 1034 // Reposting tests 1035 // repost 1 - vanilla repost 1036 { 1037 submitPost( 1038 10, /*feeRateNanosPerKB*/ 1039 m1Pub, /*updaterPkBase58Check*/ 1040 m1Priv, /*updaterPrivBase58Check*/ 1041 []byte{}, /*postHashToModify*/ 1042 []byte{}, /*parentStakeID*/ 1043 &DeSoBodySchema{}, 1044 post3Hash[:], 1045 15029557050*1e9, /*tstampNanos*/ 1046 false /*isHidden*/) 1047 1048 } 1049 repost1Txn := txns[len(txns)-1] 1050 repost1Hash := repost1Txn.Hash() 1051 _, _ = repost1Txn, repost1Hash 1052 // repost 2 - vanilla repost + hide 1053 { 1054 submitPost( 1055 10, /*feeRateNanosPerKB*/ 1056 m1Pub, /*updaterPkBase58Check*/ 1057 m1Priv, /*updaterPrivBase58Check*/ 1058 []byte{}, /*postHashToModify*/ 1059 []byte{}, /*parentStakeID*/ 1060 &DeSoBodySchema{}, 1061 post4Hash[:], 1062 15029557051*1e9, /*tstampNanos*/ 1063 false /*isHidden*/) 1064 repost2Txn := txns[len(txns)-1] 1065 repost2Hash := repost2Txn.Hash() 1066 submitPost( 1067 10, /*feeRateNanosPerKB*/ 1068 m1Pub, /*updaterPkBase58Check*/ 1069 m1Priv, /*updaterPrivBase58Check*/ 1070 repost2Hash[:], /*postHashToModify*/ 1071 []byte{}, /*parentStakeID*/ 1072 &DeSoBodySchema{}, 1073 post4Hash[:], 1074 15029557052*1e9, /*tstampNanos*/ 1075 true /*isHidden*/) 1076 } 1077 // repost 3 - Quote Repost 1078 { 1079 submitPost( 1080 10, /*feeRateNanosPerKB*/ 1081 m1Pub, /*updaterPkBase58Check*/ 1082 m1Priv, /*updaterPrivBase58Check*/ 1083 []byte{}, /*postHashToModify*/ 1084 []byte{}, /*parentStakeID*/ 1085 &DeSoBodySchema{Body: "quote-post"}, 1086 post5Hash[:], 1087 15029557053*1e9, /*tstampNanos*/ 1088 false /*isHidden*/) 1089 } 1090 // repost 4 - Quote Repost + hide 1091 { 1092 submitPost( 1093 10, /*feeRateNanosPerKB*/ 1094 m1Pub, /*updaterPkBase58Check*/ 1095 m1Priv, /*updaterPrivBase58Check*/ 1096 []byte{}, /*postHashToModify*/ 1097 []byte{}, /*parentStakeID*/ 1098 &DeSoBodySchema{Body: "quote-post-hide-me"}, 1099 post6Hash[:], 1100 15029557054*1e9, /*tstampNanos*/ 1101 false /*isHidden*/) 1102 repost4Txn := txns[len(txns)-1] 1103 repost4hash := repost4Txn.Hash() 1104 submitPost( 1105 10, /*feeRateNanosPerKB*/ 1106 m1Pub, /*updaterPkBase58Check*/ 1107 m1Priv, /*updaterPrivBase58Check*/ 1108 repost4hash[:], /*postHashToModify*/ 1109 []byte{}, /*parentStakeID*/ 1110 &DeSoBodySchema{Body: "quote-post-hide-me"}, 1111 post6Hash[:], 1112 15029557054*1e9, /*tstampNanos*/ 1113 true /*isHidden*/) 1114 } 1115 // repost -- test exceptions 1116 { 1117 { 1118 // Reposting a post that doesn't exist will raise an error. 1119 _, _, _, err = _submitPost(t, chain, db, params, 1120 10, 1121 m1Pub, 1122 m1Priv, 1123 []byte{}, 1124 []byte{}, 1125 &DeSoBodySchema{}, 1126 []byte{1, 2, 3}, 1127 15029557055, 1128 false, 1129 ) 1130 require.Error(err) 1131 require.Contains(err.Error(), RuleErrorSubmitPostRepostPostNotFound) 1132 } 1133 { 1134 // Cannot repost a vanilla repost 1135 _, _, _, err = _submitPost(t, chain, db, params, 1136 10, 1137 m1Pub, 1138 m1Priv, 1139 []byte{}, 1140 []byte{}, 1141 &DeSoBodySchema{}, 1142 repost1Hash[:], 1143 15029557055, 1144 false, 1145 ) 1146 require.Error(err) 1147 require.Contains(err.Error(), RuleErrorSubmitPostRepostOfRepost) 1148 } 1149 { 1150 // Cannot update the repostedPostHashHex 1151 _, _, _, err = _submitPost(t, chain, db, params, 1152 10, 1153 m1Pub, 1154 m1Priv, 1155 repost1Hash[:], 1156 []byte{}, 1157 &DeSoBodySchema{}, 1158 post4Hash[:], 1159 15029557055, 1160 false, 1161 ) 1162 require.Error(err) 1163 require.Contains(err.Error(), RuleErrorSubmitPostUpdateRepostHash) 1164 } 1165 1166 } 1167 1168 // Swapping the identity of m0 and m1 should not result in any issues. 1169 // TODO: This will no longer be the case once posts are a part of the PKID 1170 // infrastructure. 1171 swapIdentity( 1172 10, /*feeRateNanosPerKB*/ 1173 m3Pub, // m3 is paramUpdater for this test. 1174 m3Priv, 1175 m0PkBytes, 1176 m1PkBytes) 1177 1178 // post1: m0 post body MODIFIED 1179 // post2: paramUpdater post body MODIFIED 1180 // post3: m1 post body 1 no profile modified back (isHidden = false) 1181 // post4: m2 post body MODIFIED 1182 // post5: paramUpdater post body MODIFIED 1183 // post6: paramUpdater m2 post body MODIFIED 1184 // comment1: m0 comment from m0 on post3 MODIFIED 1185 // comment2: m0 comment 2 from m0 on post3 1186 // comment3: comment from m2 on post6 MODIFIED (isHidden = true) 1187 // comment4: m3 comment 1 from m3 on post6 1188 // comment5: comment 1 from m0 on post3 MODIFIED 1189 // comment6: m1 comment m1 on profile m2 [1] 1190 // comment7: m3 comment m3 on profile m3 [1] 1191 // comment8: m0 comment m0 on profile m3 [2] 1192 // Comments for post3 1193 // - comment1 1194 // Comments for post6 1195 // - comment2, comment3, comment4 1196 // Coomments for m1 1197 // - comment5 1198 // Comments profile m2 1199 // - comment6 1200 // Comments profile m3 1201 // - comment7, comment8 1202 // - repost1 1203 // Reposts post3 1204 // - repost 2 1205 // reposts post4 and then hides itself -- test RepostCount 1206 // - repost 3 1207 // quote repost post 5 1208 // - repost 4 1209 // quote repost post 6 and then hides itself 1210 1211 comparePostBody := func(postEntry *PostEntry, message string, repostPostHash *BlockHash) { 1212 bodyJSONObj := &DeSoBodySchema{} 1213 err := json.Unmarshal(postEntry.Body, bodyJSONObj) 1214 require.NoError(err) 1215 require.Equal(message, bodyJSONObj.Body) 1216 if repostPostHash != nil { 1217 require.Equal(repostPostHash, postEntry.RepostedPostHash) 1218 } 1219 } 1220 1221 checkPostsExist := func() { 1222 utxoView, err := NewUtxoView(db, params, nil) 1223 require.NoError(err) 1224 corePosts, commentsByPostHash, err := utxoView.GetAllPosts() 1225 require.NoError(err) 1226 // 4 posts from seed txns 1227 require.Equal(14, len(corePosts)) 1228 1229 totalComments := 0 1230 for _, currentComment := range commentsByPostHash { 1231 totalComments += len(currentComment) 1232 } 1233 // 3 comments from seed txns 1234 require.Equal(11, totalComments) 1235 1236 // post3 should have 1 comment 1237 { 1238 commentsForPost, exists := commentsByPostHash[*post3Hash] 1239 require.True(exists) 1240 require.Equal(1, len(commentsForPost)) 1241 1242 require.Equal(m0PkBytes, commentsForPost[0].PosterPublicKey) 1243 require.Equal(post3Hash[:], commentsForPost[0].ParentStakeID) 1244 require.Equal(*comment1Hash, *commentsForPost[0].PostHash) 1245 comparePostBody(commentsForPost[0], "comment from m0 on post3 MODIFIED", nil) 1246 require.Equal(false, commentsForPost[0].IsHidden) 1247 1248 post3 := findPostByPostHash(corePosts, post3Hash) 1249 require.Equal(uint64(1), post3.CommentCount) 1250 } 1251 // post6 should have 3 comments 1252 { 1253 commentsForPost, err := commentsByPostHash[*post6Hash] 1254 require.True(err) 1255 require.Equal(3, len(commentsForPost)) 1256 1257 require.Equal(m0PkBytes, commentsForPost[0].PosterPublicKey) 1258 require.Equal(post6Hash[:], commentsForPost[0].ParentStakeID) 1259 require.Equal(*comment2Hash, *commentsForPost[0].PostHash) 1260 comparePostBody(commentsForPost[0], "comment 2 from m0 on post3", nil) 1261 require.Equal(false, commentsForPost[0].IsHidden) 1262 1263 require.Equal(m2PkBytes, commentsForPost[1].PosterPublicKey) 1264 require.Equal(post6Hash[:], commentsForPost[1].ParentStakeID) 1265 require.Equal(*comment3Hash, *commentsForPost[1].PostHash) 1266 comparePostBody(commentsForPost[1], "comment from m2 on post6 MODIFIED", nil) 1267 require.Equal(true, commentsForPost[1].IsHidden) 1268 1269 require.Equal(m3PkBytes, commentsForPost[2].PosterPublicKey) 1270 require.Equal(post6Hash[:], commentsForPost[2].ParentStakeID) 1271 require.Equal(*comment4Hash, *commentsForPost[2].PostHash) 1272 comparePostBody(commentsForPost[2], "comment 1 from m3 on post6", nil) 1273 require.Equal(false, commentsForPost[2].IsHidden) 1274 1275 // Two comments are not hidden, so commentCount should be 2 1276 post6 := findPostByPostHash(corePosts, post6Hash) 1277 require.Equal(uint64(2), post6.CommentCount) 1278 } 1279 // m1 should have 1 comment 1280 { 1281 commentsForPost, err := commentsByPostHash[*NewBlockHash(m1PkBytes)] 1282 require.True(err) 1283 require.Equal(1, len(commentsForPost)) 1284 1285 require.Equal(m0PkBytes, commentsForPost[0].PosterPublicKey) 1286 require.Equal(m1PkBytes[:], commentsForPost[0].ParentStakeID) 1287 require.Equal(*comment5Hash, *commentsForPost[0].PostHash) 1288 comparePostBody(commentsForPost[0], "comment 1 from m0 on post3 MODIFIED", nil) 1289 require.Equal(true, commentsForPost[0].IsHidden) 1290 } 1291 // m2 should have 1 comment 1292 { 1293 commentsForPost, err := commentsByPostHash[*NewBlockHash(m2PkBytes)] 1294 require.True(err) 1295 require.Equal(1, len(commentsForPost)) 1296 1297 require.Equal(m1PkBytes, commentsForPost[0].PosterPublicKey) 1298 require.Equal(m2PkBytes[:], commentsForPost[0].ParentStakeID) 1299 require.Equal(*comment6Hash, *commentsForPost[0].PostHash) 1300 comparePostBody(commentsForPost[0], "comment m1 on profile m2 [1]", nil) 1301 require.Equal(false, commentsForPost[0].IsHidden) 1302 } 1303 // m3 should have 2 comments 1304 { 1305 commentsForPost, err := commentsByPostHash[*NewBlockHash(m3PkBytes)] 1306 require.True(err) 1307 require.Equal(2, len(commentsForPost)) 1308 1309 require.Equal(m3PkBytes, commentsForPost[0].PosterPublicKey) 1310 require.Equal(m3PkBytes[:], commentsForPost[0].ParentStakeID) 1311 require.Equal(*comment7Hash, *commentsForPost[0].PostHash) 1312 comparePostBody(commentsForPost[0], "comment m3 on profile m3 [1]", nil) 1313 require.Equal(false, commentsForPost[0].IsHidden) 1314 1315 require.Equal(m0PkBytes, commentsForPost[1].PosterPublicKey) 1316 require.Equal(m3PkBytes[:], commentsForPost[1].ParentStakeID) 1317 require.Equal(*comment8Hash, *commentsForPost[1].PostHash) 1318 comparePostBody(commentsForPost[1], "comment m0 on profile m3 [2]", nil) 1319 require.Equal(false, commentsForPost[1].IsHidden) 1320 } 1321 1322 sort.Slice(corePosts, func(ii, jj int) bool { 1323 return corePosts[ii].TimestampNanos < corePosts[jj].TimestampNanos 1324 }) 1325 1326 { 1327 require.Equal(m0PkBytes, corePosts[0].PosterPublicKey) 1328 comparePostBody(corePosts[0], "m0 post body MODIFIED", nil) 1329 require.Equal(true, corePosts[0].IsHidden) 1330 require.Equal(int64(10*100), int64(corePosts[0].CreatorBasisPoints)) 1331 require.Equal(int64(1.25*100*100), int64(corePosts[0].StakeMultipleBasisPoints)) 1332 } 1333 { 1334 require.Equal(m0PkBytes, corePosts[1].PosterPublicKey) 1335 comparePostBody(corePosts[1], "m0 post body 2 no profile", nil) 1336 require.Equal(false, corePosts[1].IsHidden) 1337 require.Equal(int64(10*100), int64(corePosts[1].CreatorBasisPoints)) 1338 require.Equal(int64(1.25*100*100), int64(corePosts[1].StakeMultipleBasisPoints)) 1339 } 1340 { 1341 require.Equal(m1PkBytes, corePosts[2].PosterPublicKey) 1342 comparePostBody(corePosts[2], "m1 post body 1 no profile modified back", nil) 1343 require.Equal(false, corePosts[2].IsHidden) 1344 require.Equal(int64(10*100), int64(corePosts[2].CreatorBasisPoints)) 1345 require.Equal(int64(1.25*100*100), int64(corePosts[2].StakeMultipleBasisPoints)) 1346 require.Equal(int64(1), int64(corePosts[2].RepostCount)) 1347 } 1348 { 1349 require.Equal(m2PkBytes, corePosts[3].PosterPublicKey) 1350 comparePostBody(corePosts[3], "m2 post body MODIFIED", nil) 1351 require.Equal(true, corePosts[3].IsHidden) 1352 require.Equal(int64(10*100), int64(corePosts[3].CreatorBasisPoints)) 1353 require.Equal(int64(1.25*100*100), int64(corePosts[3].StakeMultipleBasisPoints)) 1354 require.Equal(int64(0), int64(corePosts[3].RepostCount)) 1355 } 1356 { 1357 require.Equal(m3PkBytes, corePosts[4].PosterPublicKey) 1358 comparePostBody(corePosts[4], "paramUpdater post body MODIFIED", nil) 1359 require.Equal(true, corePosts[4].IsHidden) 1360 require.Equal(int64(10*100), int64(corePosts[4].CreatorBasisPoints)) 1361 require.Equal(int64(1.25*100*100), int64(corePosts[4].StakeMultipleBasisPoints)) 1362 // Quote desos do not count towards repost count 1363 require.Equal(int64(0), int64(corePosts[4].RepostCount)) 1364 } 1365 { 1366 require.Equal(m2PkBytes, corePosts[5].PosterPublicKey) 1367 comparePostBody(corePosts[5], "m2 post body 2 WITH profile", nil) 1368 require.Equal(false, corePosts[5].IsHidden) 1369 require.Equal(int64(10*100), int64(corePosts[5].CreatorBasisPoints)) 1370 require.Equal(int64(1.25*100*100), int64(corePosts[5].StakeMultipleBasisPoints)) 1371 // Quote desos do not count towards repost count 1372 require.Equal(int64(0), int64(corePosts[5].RepostCount)) 1373 } 1374 { 1375 require.Equal(m1PkBytes, corePosts[10].PosterPublicKey) 1376 comparePostBody(corePosts[10], "", corePosts[2].PostHash) 1377 require.Equal(false, corePosts[10].IsHidden) 1378 m1Post2ReaderState := utxoView.GetPostEntryReaderState(m1PkBytes, corePosts[2]) 1379 require.Equal(m1Post2ReaderState.RepostedByReader, true) 1380 require.Equal(m1Post2ReaderState.RepostPostHashHex, hex.EncodeToString(corePosts[10].PostHash[:])) 1381 // Make sure the utxoView has the correct repost entry mapping 1382 require.Equal(utxoView.RepostKeyToRepostEntry[MakeRepostKey(m1PkBytes, *corePosts[2].PostHash)], 1383 &RepostEntry{ 1384 ReposterPubKey: m1PkBytes, 1385 RepostedPostHash: corePosts[2].PostHash, 1386 RepostPostHash: corePosts[10].PostHash, 1387 }) 1388 } 1389 { 1390 require.Equal(m1PkBytes, corePosts[11].PosterPublicKey) 1391 comparePostBody(corePosts[11], "", corePosts[3].PostHash) 1392 require.Equal(true, corePosts[11].IsHidden) 1393 m1Post3ReaderState := utxoView.GetPostEntryReaderState(m1PkBytes, corePosts[3]) 1394 // If we hide the repost, we expect RepostedByReader to be false, but RepostPostHashHex to still be set. 1395 require.Equal(m1Post3ReaderState.RepostedByReader, false) 1396 require.Equal(m1Post3ReaderState.RepostPostHashHex, hex.EncodeToString(corePosts[11].PostHash[:])) 1397 // Make sure the utxoView has the correct repost entry mapping 1398 require.Equal(utxoView.RepostKeyToRepostEntry[MakeRepostKey(m1PkBytes, *corePosts[3].PostHash)], 1399 &RepostEntry{ 1400 ReposterPubKey: m1PkBytes, 1401 RepostedPostHash: corePosts[3].PostHash, 1402 RepostPostHash: corePosts[11].PostHash, 1403 }) 1404 } 1405 { 1406 require.Equal(m1PkBytes, corePosts[12].PosterPublicKey) 1407 comparePostBody(corePosts[12], "quote-post", corePosts[4].PostHash) 1408 require.Equal(false, corePosts[12].IsHidden) 1409 m1Post4ReaderState := utxoView.GetPostEntryReaderState(m1PkBytes, corePosts[4]) 1410 // Quote reposts do not impact PostEntryReaderState 1411 require.Equal(m1Post4ReaderState.RepostedByReader, false) 1412 require.Equal(m1Post4ReaderState.RepostPostHashHex, "") 1413 // Quote reposts do not make repost entry mappings 1414 _, repostEntryExists := utxoView.RepostKeyToRepostEntry[MakeRepostKey(m1PkBytes, *corePosts[4].PostHash)] 1415 require.False(repostEntryExists) 1416 } 1417 { 1418 require.Equal(m1PkBytes, corePosts[13].PosterPublicKey) 1419 comparePostBody(corePosts[13], "quote-post-hide-me", corePosts[5].PostHash) 1420 require.Equal(true, corePosts[13].IsHidden) 1421 m1Post5ReaderState := utxoView.GetPostEntryReaderState(m1PkBytes, corePosts[5]) 1422 // Quote reposts do not impact PostEntryReaderState 1423 require.Equal(m1Post5ReaderState.RepostedByReader, false) 1424 require.Equal(m1Post5ReaderState.RepostPostHashHex, "") 1425 // Quote reposts do not make repost entry mappings 1426 _, repostEntryExists := utxoView.RepostKeyToRepostEntry[MakeRepostKey(m1PkBytes, *corePosts[5].PostHash)] 1427 require.False(repostEntryExists) 1428 } 1429 } 1430 checkPostsExist() 1431 1432 // =================================================================================== 1433 // Finish it off with some transactions 1434 // =================================================================================== 1435 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1436 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1437 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1438 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1439 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1440 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 1441 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1442 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1443 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1444 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1445 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1446 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 1447 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1448 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1449 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1450 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1451 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1452 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1453 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1454 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1455 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1456 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 1457 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1458 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1459 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 1460 1461 // Roll back all of the above using the utxoOps from each. 1462 for ii := 0; ii < len(txnOps); ii++ { 1463 backwardIter := len(txnOps) - 1 - ii 1464 currentOps := txnOps[backwardIter] 1465 currentTxn := txns[backwardIter] 1466 fmt.Printf("Disconnecting transaction with type %v index %d (going backwards)\n", currentTxn.TxnMeta.GetTxnType(), backwardIter) 1467 1468 utxoView, err := NewUtxoView(db, params, nil) 1469 require.NoError(err) 1470 1471 currentHash := currentTxn.Hash() 1472 err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight) 1473 require.NoError(err) 1474 1475 require.NoError(utxoView.FlushToDb()) 1476 1477 // After disconnecting, the balances should be restored to what they 1478 // were before this transaction was applied. 1479 require.Equal(expectedSenderBalances[backwardIter], _getBalance(t, chain, nil, PkToStringTestnet(currentTxn.PublicKey))) 1480 } 1481 1482 // Verify that all the profiles have been deleted. 1483 checkPostsDeleted() 1484 1485 // Apply all the transactions to a mempool object and make sure we don't get any 1486 // errors. Verify the balances align as we go. 1487 for ii, tx := range txns { 1488 // See comment above on this transaction. 1489 fmt.Printf("Adding txn %d of type %v to mempool\n", ii, tx.TxnMeta.GetTxnType()) 1490 1491 require.Equal(expectedSenderBalances[ii], _getBalance(t, chain, mempool, PkToStringTestnet(tx.PublicKey))) 1492 1493 _, err := mempool.ProcessTransaction(tx, false, false, 0, true) 1494 require.NoError(err, "Problem adding transaction %d to mempool: %v", ii, tx) 1495 } 1496 1497 // Apply all the transactions to a view and flush the view to the db. 1498 utxoView, err := NewUtxoView(db, params, nil) 1499 require.NoError(err) 1500 for ii, txn := range txns { 1501 fmt.Printf("Adding txn %v of type %v to UtxoView\n", ii, txn.TxnMeta.GetTxnType()) 1502 1503 // Assert "before" comment counts are correct at a few different spots 1504 if ii == comment2CreatedTxnIndex { 1505 assertCommentCount(utxoView, require, post6Hash, 0) 1506 } 1507 if ii == comment3CreatedTxnIndex { 1508 assertCommentCount(utxoView, require, post6Hash, 1) 1509 } 1510 if ii == comment4CreatedTxnIndex { 1511 assertCommentCount(utxoView, require, post6Hash, 2) 1512 } 1513 if ii == comment3HiddenTxnIndex { 1514 assertCommentCount(utxoView, require, post6Hash, 3) 1515 } 1516 1517 // Always use height+1 for validation since it's assumed the transaction will 1518 // get mined into the next block. 1519 txHash := txn.Hash() 1520 blockHeight := chain.blockTip().Height + 1 1521 _, _, _, _, err = 1522 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 1523 require.NoError(err) 1524 1525 // Assert "after" comment counts are correct at a few different spots 1526 if ii == comment2CreatedTxnIndex { 1527 assertCommentCount(utxoView, require, post6Hash, 1) 1528 } 1529 if ii == comment3CreatedTxnIndex { 1530 assertCommentCount(utxoView, require, post6Hash, 2) 1531 } 1532 if ii == comment4CreatedTxnIndex { 1533 assertCommentCount(utxoView, require, post6Hash, 3) 1534 } 1535 if ii == comment3HiddenTxnIndex { 1536 assertCommentCount(utxoView, require, post6Hash, 2) 1537 } 1538 } 1539 // Flush the utxoView after having added all the transactions. 1540 require.NoError(utxoView.FlushToDb()) 1541 1542 // Verify the profiles exist. 1543 checkPostsExist() 1544 1545 // Disonnect the transactions from a single view in the same way as above 1546 // i.e. without flushing each time. 1547 utxoView2, err := NewUtxoView(db, params, nil) 1548 require.NoError(err) 1549 for ii := 0; ii < len(txnOps); ii++ { 1550 backwardIter := len(txnOps) - 1 - ii 1551 fmt.Printf("Disconnecting transaction with index %d (going backwards)\n", backwardIter) 1552 currentOps := txnOps[backwardIter] 1553 currentTxn := txns[backwardIter] 1554 1555 // Assert "before" comment counts are correct at a few different spots 1556 if backwardIter == comment3HiddenTxnIndex { 1557 assertCommentCount(utxoView2, require, post6Hash, 2) 1558 } 1559 if backwardIter == comment4CreatedTxnIndex { 1560 assertCommentCount(utxoView2, require, post6Hash, 3) 1561 } 1562 if backwardIter == comment3CreatedTxnIndex { 1563 assertCommentCount(utxoView2, require, post6Hash, 2) 1564 } 1565 if backwardIter == comment2CreatedTxnIndex { 1566 assertCommentCount(utxoView2, require, post6Hash, 1) 1567 } 1568 1569 currentHash := currentTxn.Hash() 1570 err = utxoView2.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight) 1571 require.NoError(err) 1572 1573 // Assert "after" comment counts are correct at a few different spots 1574 if backwardIter == comment3HiddenTxnIndex { 1575 assertCommentCount(utxoView2, require, post6Hash, 3) 1576 } 1577 if backwardIter == comment4CreatedTxnIndex { 1578 assertCommentCount(utxoView2, require, post6Hash, 2) 1579 } 1580 if backwardIter == comment3CreatedTxnIndex { 1581 assertCommentCount(utxoView2, require, post6Hash, 1) 1582 } 1583 if backwardIter == comment2CreatedTxnIndex { 1584 assertCommentCount(utxoView2, require, post6Hash, 0) 1585 } 1586 } 1587 require.NoError(utxoView2.FlushToDb()) 1588 require.Equal(expectedSenderBalances[0], _getBalance(t, chain, nil, senderPkString)) 1589 1590 // Verify that all the profiles have been deleted. 1591 checkPostsDeleted() 1592 1593 // All the txns should be in the mempool already so mining a block should put 1594 // all those transactions in it. 1595 block, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1596 require.NoError(err) 1597 // Add one for the block reward. Now we have a meaty block. 1598 require.Equal(len(txnOps)+1, len(block.Txns)) 1599 1600 // Roll back the block and make sure we don't hit any errors. 1601 { 1602 utxoView, err := NewUtxoView(db, params, nil) 1603 require.NoError(err) 1604 1605 // Fetch the utxo operations for the block we're detaching. We need these 1606 // in order to be able to detach the block. 1607 hash, err := block.Header.Hash() 1608 require.NoError(err) 1609 utxoOps, err := GetUtxoOperationsForBlock(db, hash) 1610 require.NoError(err) 1611 1612 // Compute the hashes for all the transactions. 1613 txHashes, err := ComputeTransactionHashes(block.Txns) 1614 require.NoError(err) 1615 require.NoError(utxoView.DisconnectBlock(block, txHashes, utxoOps)) 1616 1617 // Flushing the view after applying and rolling back should work. 1618 require.NoError(utxoView.FlushToDb()) 1619 } 1620 1621 // Verify that all the profiles have been deleted. 1622 checkPostsDeleted() 1623 } 1624 1625 func assertCommentCount(utxoView *UtxoView, require *require.Assertions, postHash *BlockHash, 1626 expectedCommentCount int) { 1627 corePosts, _, err := utxoView.GetAllPosts() 1628 require.NoError(err) 1629 1630 post := findPostByPostHash(corePosts, postHash) 1631 require.Equal(uint64(expectedCommentCount), post.CommentCount) 1632 } 1633 1634 func findPostByPostHash(posts []*PostEntry, targetPostHash *BlockHash) (_targetPost *PostEntry) { 1635 var targetPost *PostEntry 1636 for _, post := range posts { 1637 if reflect.DeepEqual(post.PostHash, targetPostHash) { 1638 targetPost = post 1639 break 1640 } 1641 } 1642 return targetPost 1643 } 1644 1645 func TestDeSoDiamonds(t *testing.T) { 1646 DeSoDiamondsBlockHeight = 0 1647 diamondValueMap := GetDeSoNanosDiamondLevelMapAtBlockHeight(0) 1648 1649 assert := assert.New(t) 1650 require := require.New(t) 1651 _ = assert 1652 _ = require 1653 1654 chain, params, db := NewLowDifficultyBlockchain() 1655 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 1656 // Make m3, m4 a paramUpdater for this test 1657 params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true 1658 params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true 1659 1660 // Mine a few blocks to give the senderPkString some money. 1661 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1662 require.NoError(err) 1663 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1664 require.NoError(err) 1665 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1666 require.NoError(err) 1667 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1668 require.NoError(err) 1669 1670 // We build the testMeta obj after mining blocks so that we save the correct block height. 1671 testMeta := &TestMeta{ 1672 t: t, 1673 chain: chain, 1674 params: params, 1675 db: db, 1676 mempool: mempool, 1677 miner: miner, 1678 savedHeight: chain.blockTip().Height + 1, 1679 } 1680 1681 // Fund all the keys. 1682 _registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 1000) 1683 _registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 1000000000) 1684 _registerOrTransferWithTestMeta(testMeta, "", senderPkString, m2Pub, senderPrivString, 1000000000) 1685 1686 // Get PKIDs for looking up diamond entries. 1687 m0PkBytes, _, err := Base58CheckDecode(m0Pub) 1688 require.NoError(err) 1689 m0PKID := DBGetPKIDEntryForPublicKey(db, m0PkBytes) 1690 1691 m1PkBytes, _, err := Base58CheckDecode(m1Pub) 1692 require.NoError(err) 1693 m1PKID := DBGetPKIDEntryForPublicKey(db, m1PkBytes) 1694 1695 m2PkBytes, _, err := Base58CheckDecode(m2Pub) 1696 require.NoError(err) 1697 m2PKID := DBGetPKIDEntryForPublicKey(db, m2PkBytes) 1698 _ = m2PKID 1699 1700 validateDiamondEntry := func( 1701 senderPKID *PKID, receiverPKID *PKID, diamondPostHash *BlockHash, diamondLevel int64) { 1702 1703 diamondEntry := DbGetDiamondMappings(db, receiverPKID, senderPKID, diamondPostHash) 1704 1705 if diamondEntry == nil && diamondLevel > 0 { 1706 t.Errorf("validateDiamondEntry: couldn't find diamond entry for diamondLevel %d", diamondLevel) 1707 } else if diamondEntry == nil && diamondLevel == 0 { 1708 // If diamondLevel is set to zero, we are checking that diamondEntry is nil. 1709 return 1710 } 1711 1712 require.Equal(diamondEntry.DiamondLevel, diamondLevel) 1713 } 1714 1715 // Create a post for testing. 1716 { 1717 _submitPostWithTestMeta( 1718 testMeta, 1719 10, /*feeRateNanosPerKB*/ 1720 m0Pub, /*updaterPkBase58Check*/ 1721 m0Priv, /*updaterPrivBase58Check*/ 1722 []byte{}, /*postHashToModify*/ 1723 []byte{}, /*parentStakeID*/ 1724 &DeSoBodySchema{Body: "m0 post 1"}, /*body*/ 1725 []byte{}, 1726 1502947011*1e9, /*tstampNanos*/ 1727 false /*isHidden*/) 1728 } 1729 post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash() 1730 _ = post1Hash 1731 1732 // Have m1 give the post a diamond. 1733 { 1734 // Balances before. 1735 m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub) 1736 require.Equal(uint64(999), m0BalBeforeNFT) 1737 m1BalBeforeNFT := _getBalance(t, chain, nil, m1Pub) 1738 require.Equal(uint64(1e9), m1BalBeforeNFT) 1739 1740 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 0) 1741 _giveDeSoDiamondsWithTestMeta(testMeta, 10, m1Pub, m1Priv, post1Hash, 1) 1742 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 1) 1743 1744 // Balances after. 1745 m0BalAfterNFT := _getBalance(t, chain, nil, m0Pub) 1746 require.Equal(uint64(999+diamondValueMap[1]), m0BalAfterNFT) 1747 m1BalAfterNFT := _getBalance(t, chain, nil, m1Pub) 1748 require.Equal(uint64(1e9-diamondValueMap[1]-2), m1BalAfterNFT) 1749 } 1750 1751 // Upgrade the post from 1 -> 2 diamonds. 1752 { 1753 // Balances before. 1754 m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub) 1755 require.Equal(uint64(999+diamondValueMap[1]), m0BalBeforeNFT) 1756 m1BalBeforeNFT := _getBalance(t, chain, nil, m1Pub) 1757 require.Equal(uint64(1e9-diamondValueMap[1]-2), m1BalBeforeNFT) 1758 1759 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 1) 1760 _giveDeSoDiamondsWithTestMeta(testMeta, 10, m1Pub, m1Priv, post1Hash, 2) 1761 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 2) 1762 1763 // Balances after. 1764 m0BalAfterNFT := _getBalance(t, chain, nil, m0Pub) 1765 require.Equal(uint64(999+diamondValueMap[2]), m0BalAfterNFT) 1766 m1BalAfterNFT := _getBalance(t, chain, nil, m1Pub) 1767 require.Equal(uint64(1e9-diamondValueMap[2]-4), m1BalAfterNFT) 1768 } 1769 1770 // Upgrade the post from 2 -> 3 diamonds. 1771 { 1772 // Balances before. 1773 m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub) 1774 require.Equal(uint64(999+diamondValueMap[2]), m0BalBeforeNFT) 1775 m1BalBeforeNFT := _getBalance(t, chain, nil, m1Pub) 1776 require.Equal(uint64(1e9-diamondValueMap[2]-4), m1BalBeforeNFT) 1777 1778 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 2) 1779 _giveDeSoDiamondsWithTestMeta(testMeta, 10, m1Pub, m1Priv, post1Hash, 3) 1780 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 3) 1781 1782 // Balances after. 1783 m0BalAfterNFT := _getBalance(t, chain, nil, m0Pub) 1784 require.Equal(uint64(999+diamondValueMap[3]), m0BalAfterNFT) 1785 m1BalAfterNFT := _getBalance(t, chain, nil, m1Pub) 1786 require.Equal(uint64(1e9-diamondValueMap[3]-6), m1BalAfterNFT) 1787 } 1788 1789 // Upgrade the post from 3 -> 4 diamonds. 1790 { 1791 // Balances before. 1792 m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub) 1793 require.Equal(uint64(999+diamondValueMap[3]), m0BalBeforeNFT) 1794 m1BalBeforeNFT := _getBalance(t, chain, nil, m1Pub) 1795 require.Equal(uint64(1e9-diamondValueMap[3]-6), m1BalBeforeNFT) 1796 1797 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 3) 1798 _giveDeSoDiamondsWithTestMeta(testMeta, 10, m1Pub, m1Priv, post1Hash, 4) 1799 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 4) 1800 1801 // Balances after. 1802 m0BalAfterNFT := _getBalance(t, chain, nil, m0Pub) 1803 require.Equal(uint64(999+diamondValueMap[4]), m0BalAfterNFT) 1804 m1BalAfterNFT := _getBalance(t, chain, nil, m1Pub) 1805 require.Equal(uint64(1e9-diamondValueMap[4]-8), m1BalAfterNFT) 1806 } 1807 1808 // Upgrade the post from 4 -> 5 diamonds. 1809 { 1810 // Balances before. 1811 m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub) 1812 require.Equal(uint64(999+diamondValueMap[4]), m0BalBeforeNFT) 1813 m1BalBeforeNFT := _getBalance(t, chain, nil, m1Pub) 1814 require.Equal(uint64(1e9-diamondValueMap[4]-8), m1BalBeforeNFT) 1815 1816 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 4) 1817 _giveDeSoDiamondsWithTestMeta(testMeta, 10, m1Pub, m1Priv, post1Hash, 5) 1818 validateDiamondEntry(m1PKID.PKID, m0PKID.PKID, post1Hash, 5) 1819 1820 // Balances after. 1821 m0BalAfterNFT := _getBalance(t, chain, nil, m0Pub) 1822 require.Equal(uint64(999+diamondValueMap[5]), m0BalAfterNFT) 1823 m1BalAfterNFT := _getBalance(t, chain, nil, m1Pub) 1824 require.Equal(uint64(1e9-diamondValueMap[5]-10), m1BalAfterNFT) 1825 } 1826 1827 // Have m2 give the post 5 diamonds right off the bat. 1828 { 1829 // Balances before. 1830 m0BalBeforeNFT := _getBalance(t, chain, nil, m0Pub) 1831 require.Equal(uint64(999+diamondValueMap[5]), m0BalBeforeNFT) 1832 m2BalBeforeNFT := _getBalance(t, chain, nil, m2Pub) 1833 require.Equal(uint64(1e9), m2BalBeforeNFT) 1834 1835 validateDiamondEntry(m2PKID.PKID, m0PKID.PKID, post1Hash, 0) 1836 _giveDeSoDiamondsWithTestMeta(testMeta, 10, m2Pub, m2Priv, post1Hash, 5) 1837 validateDiamondEntry(m2PKID.PKID, m0PKID.PKID, post1Hash, 5) 1838 1839 // Balances after. 1840 m0BalAfterNFT := _getBalance(t, chain, nil, m0Pub) 1841 require.Equal(uint64(999+diamondValueMap[5]+diamondValueMap[5]), m0BalAfterNFT) 1842 m2BalAfterNFT := _getBalance(t, chain, nil, m2Pub) 1843 require.Equal(uint64(1e9-diamondValueMap[5]-2), m2BalAfterNFT) 1844 } 1845 1846 // Roll all successful txns through connect and disconnect loops to make sure nothing breaks. 1847 _rollBackTestMetaTxnsAndFlush(testMeta) 1848 _applyTestMetaTxnsToMempool(testMeta) 1849 _applyTestMetaTxnsToViewAndFlush(testMeta) 1850 _disconnectTestMetaTxnsFromViewAndFlush(testMeta) 1851 _connectBlockThenDisconnectBlockAndFlush(testMeta) 1852 } 1853 1854 func TestDeSoDiamondErrorCases(t *testing.T) { 1855 DeSoDiamondsBlockHeight = 0 1856 diamondValueMap := GetDeSoNanosDiamondLevelMapAtBlockHeight(0) 1857 1858 assert := assert.New(t) 1859 require := require.New(t) 1860 _ = assert 1861 _ = require 1862 1863 chain, params, db := NewLowDifficultyBlockchain() 1864 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 1865 // Make m3, m4 a paramUpdater for this test 1866 params.ParamUpdaterPublicKeys[MakePkMapKey(m3PkBytes)] = true 1867 params.ParamUpdaterPublicKeys[MakePkMapKey(m4PkBytes)] = true 1868 1869 // Mine a few blocks to give the senderPkString some money. 1870 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1871 require.NoError(err) 1872 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1873 require.NoError(err) 1874 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1875 require.NoError(err) 1876 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 1877 require.NoError(err) 1878 1879 // We build the testMeta obj after mining blocks so that we save the correct block height. 1880 testMeta := &TestMeta{ 1881 t: t, 1882 chain: chain, 1883 params: params, 1884 db: db, 1885 mempool: mempool, 1886 miner: miner, 1887 savedHeight: chain.blockTip().Height + 1, 1888 } 1889 1890 // Fund all the keys. 1891 _registerOrTransferWithTestMeta(testMeta, "", senderPkString, m0Pub, senderPrivString, 1000000000) 1892 _registerOrTransferWithTestMeta(testMeta, "", senderPkString, m1Pub, senderPrivString, 1000000000) 1893 1894 // Since the "CreateBasicTransferTxnWithDiamonds()" function in blockchain.go won't let us 1895 // trigger most errors that we want to check, we create another version of the function here 1896 // that allows us to put together whatever type of broken txn we want. 1897 _giveCustomDeSoDiamondTxn := func( 1898 senderPkBase58Check string, senderPrivBase58Check string, receiverPkBase58Check string, 1899 diamondPostHashBytes []byte, diamondLevel int64, amountNanos uint64) (_err error) { 1900 1901 senderPkBytes, _, err := Base58CheckDecode(senderPkBase58Check) 1902 require.NoError(err) 1903 1904 receiverPkBytes, _, err := Base58CheckDecode(receiverPkBase58Check) 1905 require.NoError(err) 1906 1907 utxoView, err := NewUtxoView(db, params, nil) 1908 require.NoError(err) 1909 1910 // Build the basic transfer txn. 1911 txn := &MsgDeSoTxn{ 1912 PublicKey: senderPkBytes, 1913 TxnMeta: &BasicTransferMetadata{}, 1914 TxOutputs: []*DeSoOutput{ 1915 { 1916 PublicKey: receiverPkBytes, 1917 AmountNanos: amountNanos, 1918 }, 1919 }, 1920 // TxInputs and TxOutputs will be set below. 1921 // This function does not compute a signature. 1922 } 1923 1924 // Make a map for the diamond extra data and add it. 1925 diamondsExtraData := make(map[string][]byte) 1926 diamondsExtraData[DiamondLevelKey] = IntToBuf(diamondLevel) 1927 diamondsExtraData[DiamondPostHashKey] = diamondPostHashBytes 1928 txn.ExtraData = diamondsExtraData 1929 1930 // We don't need to make any tweaks to the amount because it's basically 1931 // a standard "pay per kilobyte" transaction. 1932 totalInput, _, _, fees, err := 1933 chain.AddInputsAndChangeToTransaction(txn, 10, mempool) 1934 if err != nil { 1935 return errors.Wrapf( 1936 err, "giveCustomDeSoDiamondTxn: Problem adding inputs: ") 1937 } 1938 1939 // We want our transaction to have at least one input, even if it all 1940 // goes to change. This ensures that the transaction will not be "replayable." 1941 if len(txn.TxInputs) == 0 { 1942 return fmt.Errorf( 1943 "giveCustomDeSoDiamondTxn: BasicTransfer txn must have at" + 1944 " least one input but had zero inputs instead. Try increasing the fee rate.") 1945 } 1946 1947 // Sign the transaction now that its inputs are set up. 1948 _signTxn(t, txn, senderPrivBase58Check) 1949 1950 txHash := txn.Hash() 1951 // Always use height+1 for validation since it's assumed the transaction will 1952 // get mined into the next block. 1953 blockHeight := chain.blockTip().Height + 1 1954 utxoOps, totalInput, totalOutput, fees, err := 1955 utxoView.ConnectTransaction( 1956 txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 1957 if err != nil { 1958 return err 1959 } 1960 require.Equal(t, totalInput, totalOutput+fees) 1961 1962 // We should have one SPEND UtxoOperation for each input, one ADD operation 1963 // for each output, and one OperationTypeDeSoDiamond operation at the end. 1964 require.Equal(t, len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 1965 for ii := 0; ii < len(txn.TxInputs); ii++ { 1966 require.Equal(t, OperationTypeSpendUtxo, utxoOps[ii].Type) 1967 } 1968 require.Equal(OperationTypeDeSoDiamond, utxoOps[len(utxoOps)-1].Type) 1969 1970 require.NoError(utxoView.FlushToDb()) 1971 1972 return nil 1973 } 1974 1975 // Error case: PostHash with bad length. 1976 { 1977 err := _giveCustomDeSoDiamondTxn( 1978 m0Pub, 1979 m0Priv, 1980 m1Pub, 1981 RandomBytes(HashSizeBytes-1), 1982 1, 1983 diamondValueMap[1], 1984 ) 1985 require.Error(err) 1986 require.Contains(err.Error(), RuleErrorBasicTransferDiamondInvalidLengthForPostHashBytes) 1987 } 1988 1989 // Error case: non-existent post. 1990 { 1991 err := _giveCustomDeSoDiamondTxn( 1992 m0Pub, 1993 m0Priv, 1994 m1Pub, 1995 RandomBytes(HashSizeBytes), 1996 1, 1997 diamondValueMap[1], 1998 ) 1999 require.Error(err) 2000 require.Contains(err.Error(), RuleErrorBasicTransferDiamondPostEntryDoesNotExist) 2001 } 2002 2003 // Create a post for testing. 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 1"}, /*body*/ 2013 []byte{}, 2014 1502947011*1e9, /*tstampNanos*/ 2015 false /*isHidden*/) 2016 } 2017 post1Hash := testMeta.txns[len(testMeta.txns)-1].Hash() 2018 _ = post1Hash 2019 2020 // Error case: cannot diamond yourself. 2021 { 2022 err := _giveCustomDeSoDiamondTxn( 2023 m0Pub, 2024 m0Priv, 2025 m1Pub, 2026 post1Hash[:], 2027 1, 2028 diamondValueMap[1], 2029 ) 2030 require.Error(err) 2031 require.Contains(err.Error(), RuleErrorBasicTransferDiamondCannotTransferToSelf) 2032 } 2033 2034 // Error case: don't include diamond level. 2035 { 2036 _, _, _, err := _giveDeSoDiamonds( 2037 t, chain, db, params, 2038 10, 2039 m1Pub, 2040 m1Priv, 2041 post1Hash, 2042 1, 2043 true, /*deleteDiamondLevel*/ 2044 ) 2045 require.Error(err) 2046 require.Contains(err.Error(), RuleErrorBasicTransferHasDiamondPostHashWithoutDiamondLevel) 2047 } 2048 2049 // Error case: invalid diamond level. 2050 { 2051 err := _giveCustomDeSoDiamondTxn( 2052 m1Pub, 2053 m1Priv, 2054 m0Pub, 2055 post1Hash[:], 2056 -1, 2057 diamondValueMap[1], 2058 ) 2059 require.Error(err) 2060 require.Contains(err.Error(), RuleErrorBasicTransferHasInvalidDiamondLevel) 2061 } 2062 2063 // Error case: insufficient deso. 2064 { 2065 err := _giveCustomDeSoDiamondTxn( 2066 m1Pub, 2067 m1Priv, 2068 m0Pub, 2069 post1Hash[:], 2070 2, 2071 diamondValueMap[1], 2072 ) 2073 require.Error(err) 2074 require.Contains(err.Error(), RuleErrorBasicTransferInsufficientDeSoForDiamondLevel) 2075 } 2076 }