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