github.com/deso-protocol/core@v1.2.9/lib/block_view_like_test.go (about) 1 package lib 2 3 import ( 4 "fmt" 5 "github.com/dgraph-io/badger/v3" 6 "github.com/stretchr/testify/assert" 7 "github.com/stretchr/testify/require" 8 "testing" 9 ) 10 11 func _doLikeTxn(t *testing.T, chain *Blockchain, db *badger.DB, 12 params *DeSoParams, feeRateNanosPerKB uint64, senderPkBase58Check string, 13 likedPostHash BlockHash, senderPrivBase58Check string, isUnfollow bool) ( 14 _utxoOps []*UtxoOperation, _txn *MsgDeSoTxn, _height uint32, _err error) { 15 16 assert := assert.New(t) 17 require := require.New(t) 18 _ = assert 19 _ = require 20 21 senderPkBytes, _, err := Base58CheckDecode(senderPkBase58Check) 22 require.NoError(err) 23 24 utxoView, err := NewUtxoView(db, params, nil) 25 require.NoError(err) 26 27 txn, totalInputMake, changeAmountMake, feesMake, err := chain.CreateLikeTxn( 28 senderPkBytes, likedPostHash, isUnfollow, feeRateNanosPerKB, nil, []*DeSoOutput{}) 29 if err != nil { 30 return nil, nil, 0, err 31 } 32 33 require.Equal(totalInputMake, changeAmountMake+feesMake) 34 35 // Sign the transaction now that its inputs are set up. 36 _signTxn(t, txn, senderPrivBase58Check) 37 38 txHash := txn.Hash() 39 // Always use height+1 for validation since it's assumed the transaction will 40 // get mined into the next block. 41 blockHeight := chain.blockTip().Height + 1 42 utxoOps, totalInput, totalOutput, fees, err := 43 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true, /*verifySignature*/ 44 false /*ignoreUtxos*/) 45 // ConnectTransaction should treat the amount locked as contributing to the 46 // output. 47 if err != nil { 48 return nil, nil, 0, err 49 } 50 require.Equal(totalInput, totalOutput+fees) 51 require.Equal(totalInput, totalInputMake) 52 53 // We should have one SPEND UtxoOperation for each input, one ADD operation 54 // for each output, and one OperationTypeLike operation at the end. 55 require.Equal(len(txn.TxInputs)+len(txn.TxOutputs)+1, len(utxoOps)) 56 for ii := 0; ii < len(txn.TxInputs); ii++ { 57 require.Equal(OperationTypeSpendUtxo, utxoOps[ii].Type) 58 } 59 require.Equal(OperationTypeLike, utxoOps[len(utxoOps)-1].Type) 60 61 require.NoError(utxoView.FlushToDb()) 62 63 return utxoOps, txn, blockHeight, nil 64 } 65 66 func TestLikeTxns(t *testing.T) { 67 assert := assert.New(t) 68 require := require.New(t) 69 _ = assert 70 _ = require 71 72 chain, params, db := NewLowDifficultyBlockchain() 73 mempool, miner := NewTestMiner(t, chain, params, true /*isSender*/) 74 75 // Mine a few blocks to give the senderPkString some money. 76 _, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 77 require.NoError(err) 78 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 79 require.NoError(err) 80 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 81 require.NoError(err) 82 _, err = miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 83 require.NoError(err) 84 85 // Setup some convenience functions for the test. 86 txnOps := [][]*UtxoOperation{} 87 txns := []*MsgDeSoTxn{} 88 expectedSenderBalances := []uint64{} 89 expectedRecipientBalances := []uint64{} 90 91 // We take the block tip to be the blockchain height rather than the 92 // header chain height. 93 savedHeight := chain.blockTip().Height + 1 94 registerOrTransfer := func(username string, 95 senderPk string, recipientPk string, senderPriv string) { 96 97 expectedSenderBalances = append( 98 expectedSenderBalances, _getBalance(t, chain, nil, senderPkString)) 99 expectedRecipientBalances = append( 100 expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString)) 101 102 currentOps, currentTxn, _ := _doBasicTransferWithViewFlush( 103 t, chain, db, params, senderPk, recipientPk, 104 senderPriv, 7 /*amount to send*/, 11 /*feerate*/) 105 106 txnOps = append(txnOps, currentOps) 107 txns = append(txns, currentTxn) 108 } 109 110 // Fund all the keys. 111 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 112 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 113 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 114 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 115 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 116 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 117 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 118 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 119 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 120 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 121 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 122 registerOrTransfer("", senderPkString, m2Pub, senderPrivString) 123 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 124 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 125 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 126 registerOrTransfer("", senderPkString, m3Pub, senderPrivString) 127 128 doLikeTxn := func( 129 senderPkBase58Check string, likedPostHash BlockHash, 130 senderPrivBase58Check string, isUnfollow bool, feeRateNanosPerKB uint64) { 131 132 expectedSenderBalances = append( 133 expectedSenderBalances, _getBalance(t, chain, nil, senderPkString)) 134 expectedRecipientBalances = append( 135 expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString)) 136 137 currentOps, currentTxn, _, err := _doLikeTxn( 138 t, chain, db, params, feeRateNanosPerKB, senderPkBase58Check, 139 likedPostHash, senderPrivBase58Check, isUnfollow) 140 require.NoError(err) 141 142 txnOps = append(txnOps, currentOps) 143 txns = append(txns, currentTxn) 144 } 145 146 submitPost := func( 147 feeRateNanosPerKB uint64, updaterPkBase58Check string, 148 updaterPrivBase58Check string, 149 postHashToModify []byte, 150 parentStakeID []byte, 151 bodyObj *DeSoBodySchema, 152 repostedPostHash []byte, 153 tstampNanos uint64, 154 isHidden bool) { 155 156 expectedSenderBalances = append( 157 expectedSenderBalances, _getBalance(t, chain, nil, senderPkString)) 158 expectedRecipientBalances = append( 159 expectedRecipientBalances, _getBalance(t, chain, nil, recipientPkString)) 160 161 currentOps, currentTxn, _, err := _submitPost( 162 t, chain, db, params, feeRateNanosPerKB, 163 updaterPkBase58Check, 164 updaterPrivBase58Check, 165 postHashToModify, 166 parentStakeID, 167 bodyObj, 168 repostedPostHash, 169 tstampNanos, 170 isHidden) 171 172 require.NoError(err) 173 174 txnOps = append(txnOps, currentOps) 175 txns = append(txns, currentTxn) 176 } 177 178 fakePostHash := BlockHash{ 179 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 180 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 181 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 182 0x30, 0x31, 183 } 184 // Attempting "m0 -> fakePostHash" should fail since the post doesn't exist. 185 _, _, _, err = _doLikeTxn( 186 t, chain, db, params, 10 /*feeRateNanosPerKB*/, m0Pub, 187 fakePostHash, m0Priv, false /*isUnfollow*/) 188 require.Error(err) 189 require.Contains(err.Error(), RuleErrorCannotLikeNonexistentPost) 190 191 submitPost( 192 10, /*feeRateNanosPerKB*/ 193 m0Pub, /*updaterPkBase58Check*/ 194 m0Priv, /*updaterPrivBase58Check*/ 195 []byte{}, /*postHashToModify*/ 196 []byte{}, /*parentStakeID*/ 197 &DeSoBodySchema{Body: "m0 post body 1 no profile"}, /*body*/ 198 []byte{}, 199 1602947011*1e9, /*tstampNanos*/ 200 false /*isHidden*/) 201 post1Txn := txns[len(txns)-1] 202 post1Hash := *post1Txn.Hash() 203 204 { 205 submitPost( 206 10, /*feeRateNanosPerKB*/ 207 m0Pub, /*updaterPkBase58Check*/ 208 m0Priv, /*updaterPrivBase58Check*/ 209 []byte{}, /*postHashToModify*/ 210 []byte{}, /*parentStakeID*/ 211 &DeSoBodySchema{Body: "m0 post body 2 no profile"}, /*body*/ 212 []byte{}, 213 1502947012*1e9, /*tstampNanos*/ 214 false /*isHidden*/) 215 } 216 post2Txn := txns[len(txns)-1] 217 post2Hash := *post2Txn.Hash() 218 219 { 220 submitPost( 221 10, /*feeRateNanosPerKB*/ 222 m1Pub, /*updaterPkBase58Check*/ 223 m1Priv, /*updaterPrivBase58Check*/ 224 []byte{}, /*postHashToModify*/ 225 []byte{}, /*parentStakeID*/ 226 &DeSoBodySchema{Body: "m1 post body 1 no profile"}, /*body*/ 227 []byte{}, 228 1502947013*1e9, /*tstampNanos*/ 229 false /*isHidden*/) 230 } 231 post3Txn := txns[len(txns)-1] 232 post3Hash := *post3Txn.Hash() 233 234 // m0 -> p1 235 doLikeTxn(m0Pub, post1Hash, m0Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/) 236 237 // Duplicating "m0 -> p1" should fail. 238 _, _, _, err = _doLikeTxn( 239 t, chain, db, params, 10 /*feeRateNanosPerKB*/, m0Pub, 240 post1Hash, m0Priv, false /*isUnfollow*/) 241 require.Error(err) 242 require.Contains(err.Error(), RuleErrorLikeEntryAlreadyExists) 243 244 // m2 -> p1 245 doLikeTxn(m2Pub, post1Hash, m2Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/) 246 247 // m3 -> p1 248 doLikeTxn(m3Pub, post1Hash, m3Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/) 249 250 // m3 -> p2 251 doLikeTxn(m3Pub, post2Hash, m3Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/) 252 253 // m1 -> p2 254 doLikeTxn(m1Pub, post2Hash, m1Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/) 255 256 // m2 -> p3 257 doLikeTxn(m2Pub, post3Hash, m2Priv, false /*isUnfollow*/, 10 /*feeRateNanosPerKB*/) 258 259 likingP1 := [][]byte{ 260 _strToPk(t, m0Pub), 261 _strToPk(t, m2Pub), 262 _strToPk(t, m3Pub), 263 } 264 265 likingP2 := [][]byte{ 266 _strToPk(t, m1Pub), 267 _strToPk(t, m3Pub), 268 } 269 270 likingP3 := [][]byte{ 271 _strToPk(t, m2Pub), 272 } 273 274 // Verify pks liking p1 and check like count. 275 { 276 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post1Hash) 277 require.NoError(err) 278 require.Equal(len(likingP1), len(likingPks)) 279 for ii := 0; ii < len(likingPks); ii++ { 280 require.Contains(likingP1, likingPks[ii]) 281 } 282 post1 := DBGetPostEntryByPostHash(db, &post1Hash) 283 require.Equal(uint64(len(likingP1)), post1.LikeCount) 284 } 285 286 // Verify pks liking p2 and check like count. 287 { 288 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post2Hash) 289 require.NoError(err) 290 require.Equal(len(likingP2), len(likingPks)) 291 for ii := 0; ii < len(likingPks); ii++ { 292 require.Contains(likingP2, likingPks[ii]) 293 } 294 post2 := DBGetPostEntryByPostHash(db, &post2Hash) 295 require.Equal(uint64(len(likingP2)), post2.LikeCount) 296 } 297 298 // Verify pks liking p3 and check like count. 299 { 300 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post3Hash) 301 require.NoError(err) 302 require.Equal(len(likingP3), len(likingPks)) 303 for ii := 0; ii < len(likingPks); ii++ { 304 require.Contains(likingP3, likingPks[ii]) 305 } 306 post3 := DBGetPostEntryByPostHash(db, &post3Hash) 307 require.Equal(uint64(len(likingP3)), post3.LikeCount) 308 } 309 310 m0Likes := []BlockHash{ 311 post1Hash, 312 } 313 314 m1Likes := []BlockHash{ 315 post2Hash, 316 } 317 318 m2Likes := []BlockHash{ 319 post1Hash, 320 post3Hash, 321 } 322 323 m3Likes := []BlockHash{ 324 post1Hash, 325 post2Hash, 326 } 327 328 // Verify m0's likes. 329 { 330 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m0Pub)) 331 require.NoError(err) 332 require.Equal(len(m0Likes), len(likedPostHashes)) 333 for ii := 0; ii < len(likedPostHashes); ii++ { 334 require.Contains(m0Likes, *likedPostHashes[ii]) 335 } 336 } 337 338 // Verify m1's likes. 339 { 340 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m1Pub)) 341 require.NoError(err) 342 require.Equal(len(m1Likes), len(likedPostHashes)) 343 for ii := 0; ii < len(likedPostHashes); ii++ { 344 require.Contains(m1Likes, *likedPostHashes[ii]) 345 } 346 } 347 348 // Verify m2's likes. 349 { 350 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m2Pub)) 351 require.NoError(err) 352 require.Equal(len(m2Likes), len(likedPostHashes)) 353 for ii := 0; ii < len(likedPostHashes); ii++ { 354 require.Contains(m2Likes, *likedPostHashes[ii]) 355 } 356 } 357 358 // Verify m3's likes. 359 { 360 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m3Pub)) 361 require.NoError(err) 362 require.Equal(len(m3Likes), len(likedPostHashes)) 363 for ii := 0; ii < len(likedPostHashes); ii++ { 364 require.Contains(m3Likes, *likedPostHashes[ii]) 365 } 366 } 367 368 // Try an "unlike." 369 // 370 // m0 -> p1 (unfollow) 371 doLikeTxn(m0Pub, post1Hash, m0Priv, true /*isUnfollow*/, 10 /*feeRateNanosPerKB*/) 372 373 // m3 -> p2 (unfollow) 374 doLikeTxn(m3Pub, post2Hash, m3Priv, true /*isUnfollow*/, 10 /*feeRateNanosPerKB*/) 375 376 // Duplicating "m0 -> p1" (unfollow) should fail. 377 _, _, _, err = _doLikeTxn( 378 t, chain, db, params, 10 /*feeRateNanosPerKB*/, m0Pub, 379 post1Hash, m0Priv, true /*isUnfollow*/) 380 require.Error(err) 381 require.Contains(err.Error(), RuleErrorCannotUnlikeWithoutAnExistingLike) 382 383 likingP1 = [][]byte{ 384 _strToPk(t, m2Pub), 385 _strToPk(t, m3Pub), 386 } 387 388 likingP2 = [][]byte{ 389 _strToPk(t, m1Pub), 390 } 391 392 // Verify pks liking p1 and check like count. 393 { 394 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post1Hash) 395 require.NoError(err) 396 require.Equal(len(likingP1), len(likingPks)) 397 for ii := 0; ii < len(likingPks); ii++ { 398 require.Contains(likingP1, likingPks[ii]) 399 } 400 post1 := DBGetPostEntryByPostHash(db, &post1Hash) 401 require.Equal(uint64(len(likingP1)), post1.LikeCount) 402 } 403 404 // Verify pks liking p2 and check like count. 405 { 406 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post2Hash) 407 require.NoError(err) 408 require.Equal(len(likingP2), len(likingPks)) 409 for ii := 0; ii < len(likingPks); ii++ { 410 require.Contains(likingP2, likingPks[ii]) 411 } 412 post2 := DBGetPostEntryByPostHash(db, &post2Hash) 413 require.Equal(uint64(len(likingP2)), post2.LikeCount) 414 } 415 416 m3Likes = []BlockHash{ 417 post1Hash, 418 } 419 420 // Verify m0 has no likes. 421 { 422 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m0Pub)) 423 require.NoError(err) 424 require.Equal(0, len(likedPostHashes)) 425 } 426 427 // Verify m3's likes. 428 { 429 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m3Pub)) 430 require.NoError(err) 431 require.Equal(len(m3Likes), len(likedPostHashes)) 432 for i := 0; i < len(likedPostHashes); i++ { 433 require.Contains(m3Likes, *likedPostHashes[i]) 434 } 435 } 436 437 // =================================================================================== 438 // Finish it off with some transactions 439 // =================================================================================== 440 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 441 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 442 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 443 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 444 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 445 registerOrTransfer("", senderPkString, m0Pub, senderPrivString) 446 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 447 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 448 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 449 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 450 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 451 registerOrTransfer("", senderPkString, m1Pub, senderPrivString) 452 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 453 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 454 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 455 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 456 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 457 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 458 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 459 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 460 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 461 registerOrTransfer("", m1Pub, m0Pub, m1Priv) 462 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 463 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 464 registerOrTransfer("", m0Pub, m1Pub, m0Priv) 465 466 // Roll back all of the above using the utxoOps from each. 467 for ii := 0; ii < len(txnOps); ii++ { 468 backwardIter := len(txnOps) - 1 - ii 469 currentOps := txnOps[backwardIter] 470 currentTxn := txns[backwardIter] 471 fmt.Printf( 472 "Disconnecting transaction with type %v index %d (going backwards)\n", 473 currentTxn.TxnMeta.GetTxnType(), backwardIter) 474 475 utxoView, err := NewUtxoView(db, params, nil) 476 require.NoError(err) 477 478 currentHash := currentTxn.Hash() 479 err = utxoView.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight) 480 require.NoError(err) 481 482 require.NoError(utxoView.FlushToDb()) 483 484 // After disconnecting, the balances should be restored to what they 485 // were before this transaction was applied. 486 require.Equal( 487 int64(expectedSenderBalances[backwardIter]), 488 int64(_getBalance(t, chain, nil, senderPkString))) 489 require.Equal( 490 expectedRecipientBalances[backwardIter], 491 _getBalance(t, chain, nil, recipientPkString)) 492 493 // Here we check the like counts after all the like entries have been disconnected. 494 if backwardIter == 19 { 495 post1 := DBGetPostEntryByPostHash(db, &post1Hash) 496 require.Equal(uint64(0), post1.LikeCount) 497 post2 := DBGetPostEntryByPostHash(db, &post2Hash) 498 require.Equal(uint64(0), post2.LikeCount) 499 post3 := DBGetPostEntryByPostHash(db, &post3Hash) 500 require.Equal(uint64(0), post3.LikeCount) 501 } 502 } 503 504 testDisconnectedState := func() { 505 // Verify that all the pks liking each post hash have been deleted and like count == 0. 506 { 507 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post1Hash) 508 require.NoError(err) 509 require.Equal(0, len(likingPks)) 510 } 511 { 512 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post2Hash) 513 require.NoError(err) 514 require.Equal(0, len(likingPks)) 515 } 516 { 517 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post3Hash) 518 require.NoError(err) 519 require.Equal(0, len(likingPks)) 520 } 521 522 // Verify that all the post hashes liked by users have been deleted. 523 { 524 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m0Pub)) 525 require.NoError(err) 526 require.Equal(0, len(likedPostHashes)) 527 } 528 { 529 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m1Pub)) 530 require.NoError(err) 531 require.Equal(0, len(likedPostHashes)) 532 } 533 { 534 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m2Pub)) 535 require.NoError(err) 536 require.Equal(0, len(likedPostHashes)) 537 } 538 { 539 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m3Pub)) 540 require.NoError(err) 541 require.Equal(0, len(likedPostHashes)) 542 } 543 } 544 testDisconnectedState() 545 546 // Apply all the transactions to a mempool object and make sure we don't get any 547 // errors. Verify the balances align as we go. 548 for ii, tx := range txns { 549 // See comment above on this transaction. 550 fmt.Printf("Adding txn %d of type %v to mempool\n", ii, tx.TxnMeta.GetTxnType()) 551 552 require.Equal(expectedSenderBalances[ii], _getBalance(t, chain, mempool, senderPkString)) 553 require.Equal(expectedRecipientBalances[ii], _getBalance(t, chain, mempool, recipientPkString)) 554 555 _, err := mempool.ProcessTransaction(tx, false, false, 0, true) 556 require.NoError(err, "Problem adding transaction %d to mempool: %v", ii, tx) 557 } 558 559 // Apply all the transactions to a view and flush the view to the db. 560 utxoView, err := NewUtxoView(db, params, nil) 561 require.NoError(err) 562 for ii, txn := range txns { 563 fmt.Printf("Adding txn %v of type %v to UtxoView\n", ii, txn.TxnMeta.GetTxnType()) 564 565 // Always use height+1 for validation since it's assumed the transaction will 566 // get mined into the next block. 567 txHash := txn.Hash() 568 blockHeight := chain.blockTip().Height + 1 569 _, _, _, _, err := 570 utxoView.ConnectTransaction(txn, txHash, getTxnSize(*txn), blockHeight, true /*verifySignature*/, false /*ignoreUtxos*/) 571 require.NoError(err) 572 } 573 // Flush the utxoView after having added all the transactions. 574 require.NoError(utxoView.FlushToDb()) 575 576 testConnectedState := func() { 577 likingP1 = [][]byte{ 578 _strToPk(t, m2Pub), 579 _strToPk(t, m3Pub), 580 } 581 582 likingP2 = [][]byte{ 583 _strToPk(t, m1Pub), 584 } 585 586 likingP3 := [][]byte{ 587 _strToPk(t, m2Pub), 588 } 589 590 // Verify pks liking p1 and check like count. 591 { 592 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post1Hash) 593 require.NoError(err) 594 require.Equal(len(likingP1), len(likingPks)) 595 for ii := 0; ii < len(likingPks); ii++ { 596 require.Contains(likingP1, likingPks[ii]) 597 } 598 post1 := DBGetPostEntryByPostHash(db, &post1Hash) 599 require.Equal(uint64(len(likingP1)), post1.LikeCount) 600 } 601 602 // Verify pks liking p2 and check like count. 603 { 604 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post2Hash) 605 require.NoError(err) 606 require.Equal(len(likingP2), len(likingPks)) 607 for ii := 0; ii < len(likingPks); ii++ { 608 require.Contains(likingP2, likingPks[ii]) 609 } 610 post2 := DBGetPostEntryByPostHash(db, &post2Hash) 611 require.Equal(uint64(len(likingP2)), post2.LikeCount) 612 } 613 614 // Verify pks liking p3 and check like count. 615 { 616 likingPks, err := DbGetLikerPubKeysLikingAPostHash(db, post3Hash) 617 require.NoError(err) 618 require.Equal(len(likingP3), len(likingPks)) 619 for ii := 0; ii < len(likingPks); ii++ { 620 require.Contains(likingP3, likingPks[ii]) 621 } 622 post3 := DBGetPostEntryByPostHash(db, &post3Hash) 623 require.Equal(uint64(len(likingP3)), post3.LikeCount) 624 } 625 626 m1Likes := []BlockHash{ 627 post2Hash, 628 } 629 630 m2Likes := []BlockHash{ 631 post1Hash, 632 post3Hash, 633 } 634 635 m3Likes = []BlockHash{ 636 post1Hash, 637 } 638 639 // Verify m0 has no likes. 640 { 641 followPks, err := DbGetPostHashesYouLike(db, _strToPk(t, m0Pub)) 642 require.NoError(err) 643 require.Equal(0, len(followPks)) 644 } 645 646 // Verify m1's likes. 647 { 648 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m1Pub)) 649 require.NoError(err) 650 require.Equal(len(m1Likes), len(likedPostHashes)) 651 for ii := 0; ii < len(likedPostHashes); ii++ { 652 require.Contains(m1Likes, *likedPostHashes[ii]) 653 } 654 } 655 656 // Verify m2's likes. 657 { 658 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m2Pub)) 659 require.NoError(err) 660 require.Equal(len(m2Likes), len(likedPostHashes)) 661 for ii := 0; ii < len(likedPostHashes); ii++ { 662 require.Contains(m2Likes, *likedPostHashes[ii]) 663 } 664 } 665 666 // Verify m3's likes. 667 { 668 likedPostHashes, err := DbGetPostHashesYouLike(db, _strToPk(t, m3Pub)) 669 require.NoError(err) 670 require.Equal(len(m3Likes), len(likedPostHashes)) 671 for ii := 0; ii < len(likedPostHashes); ii++ { 672 require.Contains(m3Likes, *likedPostHashes[ii]) 673 } 674 } 675 } 676 testConnectedState() 677 678 // Disconnect the transactions from a single view in the same way as above 679 // i.e. without flushing each time. 680 utxoView2, err := NewUtxoView(db, params, nil) 681 require.NoError(err) 682 for ii := 0; ii < len(txnOps); ii++ { 683 backwardIter := len(txnOps) - 1 - ii 684 fmt.Printf("Disconnecting transaction with index %d (going backwards)\n", backwardIter) 685 currentOps := txnOps[backwardIter] 686 currentTxn := txns[backwardIter] 687 688 currentHash := currentTxn.Hash() 689 err = utxoView2.DisconnectTransaction(currentTxn, currentHash, currentOps, savedHeight) 690 require.NoError(err) 691 } 692 require.NoError(utxoView2.FlushToDb()) 693 require.Equal(expectedSenderBalances[0], _getBalance(t, chain, nil, senderPkString)) 694 require.Equal(expectedRecipientBalances[0], _getBalance(t, chain, nil, recipientPkString)) 695 696 testDisconnectedState() 697 698 // All the txns should be in the mempool already so mining a block should put 699 // all those transactions in it. 700 block, err := miner.MineAndProcessSingleBlock(0 /*threadIndex*/, mempool) 701 require.NoError(err) 702 // Add one for the block reward. Now we have a meaty block. 703 require.Equal(len(txnOps)+1, len(block.Txns)) 704 // Estimate the transaction fees of the tip block in various ways. 705 { 706 // Threshold above what's in the block should return the default fee at all times. 707 require.Equal(int64(0), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 0))) 708 require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.1, 7))) 709 // Threshold below what's in the block should return the max of the median 710 // and the minfee. This means with a low minfee the value returned should be 711 // higher. And with a high minfee the value returned should be equal to the 712 // fee. 713 require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 7))) 714 require.Equal(int64(4), int64(chain.EstimateDefaultFeeRateNanosPerKB(0, 0))) 715 require.Equal(int64(7), int64(chain.EstimateDefaultFeeRateNanosPerKB(.01, 7))) 716 require.Equal(int64(4), int64(chain.EstimateDefaultFeeRateNanosPerKB(.01, 1))) 717 } 718 719 testConnectedState() 720 721 // Roll back the block and make sure we don't hit any errors. 722 { 723 utxoView, err := NewUtxoView(db, params, nil) 724 require.NoError(err) 725 726 // Fetch the utxo operations for the block we're detaching. We need these 727 // in order to be able to detach the block. 728 hash, err := block.Header.Hash() 729 require.NoError(err) 730 utxoOps, err := GetUtxoOperationsForBlock(db, hash) 731 require.NoError(err) 732 733 // Compute the hashes for all the transactions. 734 txHashes, err := ComputeTransactionHashes(block.Txns) 735 require.NoError(err) 736 require.NoError(utxoView.DisconnectBlock(block, txHashes, utxoOps)) 737 738 // Flushing the view after applying and rolling back should work. 739 require.NoError(utxoView.FlushToDb()) 740 } 741 742 testDisconnectedState() 743 }