github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/modules/transactionpool/update_test.go (about) 1 package transactionpool 2 3 import ( 4 "sort" 5 "testing" 6 "time" 7 8 "SiaPrime/modules" 9 "SiaPrime/types" 10 ) 11 12 // TestFindSets checks that the findSets functions is properly parsing and 13 // combining transactions into their minimal sets. 14 func TestFindSets(t *testing.T) { 15 // Graph a graph which is a chain. Graph will be invalid, but we don't need 16 // the consensus set, so no worries. 17 graph1Size := 5 18 edges := make([]types.TransactionGraphEdge, 0, graph1Size) 19 for i := 0; i < graph1Size; i++ { 20 edges = append(edges, types.TransactionGraphEdge{ 21 Dest: i + 1, 22 Fee: types.NewCurrency64(5), 23 Source: i, 24 Value: types.NewCurrency64(100), 25 }) 26 } 27 graph1, err := types.TransactionGraph(types.SiacoinOutputID{}, edges) 28 if err != nil { 29 t.Fatal(err) 30 } 31 32 // Split the graph using findSets. Result should be a single set with 5 33 // transactions. 34 sets := findSets(graph1) 35 if len(sets) != 1 { 36 t.Fatal("there should be only one set") 37 } 38 if len(sets[0]) != graph1Size { 39 t.Error("findSets is not grouping the transactions correctly") 40 } 41 42 // Create a second graph to check it can handle two graphs. 43 graph2Size := 6 44 edges = make([]types.TransactionGraphEdge, 0, graph2Size) 45 for i := 0; i < graph2Size; i++ { 46 edges = append(edges, types.TransactionGraphEdge{ 47 Dest: i + 1, 48 Fee: types.NewCurrency64(5), 49 Source: i, 50 Value: types.NewCurrency64(100), 51 }) 52 } 53 graph2, err := types.TransactionGraph(types.SiacoinOutputID{1}, edges) 54 if err != nil { 55 t.Fatal(err) 56 } 57 sets = findSets(append(graph1, graph2...)) 58 if len(sets) != 2 { 59 t.Fatal("there should be two sets") 60 } 61 lens := []int{len(sets[0]), len(sets[1])} 62 sort.Ints(lens) 63 expected := []int{graph1Size, graph2Size} 64 sort.Ints(expected) 65 if lens[0] != expected[0] || lens[1] != expected[1] { 66 t.Error("Resulting sets do not have the right lengths") 67 } 68 69 // Create a diamond graph to make sure it can handle diamond graph. 70 edges = make([]types.TransactionGraphEdge, 0, 5) 71 sources := []int{0, 0, 1, 2, 3} 72 dests := []int{1, 2, 3, 3, 4} 73 for i := 0; i < 5; i++ { 74 edges = append(edges, types.TransactionGraphEdge{ 75 Dest: dests[i], 76 Fee: types.NewCurrency64(5), 77 Source: sources[i], 78 Value: types.NewCurrency64(100), 79 }) 80 } 81 graph3, err := types.TransactionGraph(types.SiacoinOutputID{2}, edges) 82 graph3Size := len(graph3) 83 if err != nil { 84 t.Fatal(err) 85 } 86 sets = findSets(append(graph1, append(graph2, graph3...)...)) 87 if len(sets) != 3 { 88 t.Fatal("there should be two sets") 89 } 90 lens = []int{len(sets[0]), len(sets[1]), len(sets[2])} 91 sort.Ints(lens) 92 expected = []int{graph1Size, graph2Size, graph3Size} 93 sort.Ints(expected) 94 if lens[0] != expected[0] || lens[1] != expected[1] || lens[2] != expected[2] { 95 t.Error("Resulting sets do not have the right lengths") 96 } 97 98 // Sporadically weave the transactions and make sure the set finder still 99 // parses the sets correctly (sets can assumed to be ordered, but not all in 100 // a row). 101 var sporadic []types.Transaction 102 for len(graph1) > 0 || len(graph2) > 0 || len(graph3) > 0 { 103 if len(graph1) > 0 { 104 sporadic = append(sporadic, graph1[0]) 105 graph1 = graph1[1:] 106 } 107 if len(graph2) > 0 { 108 sporadic = append(sporadic, graph2[0]) 109 graph2 = graph2[1:] 110 } 111 if len(graph3) > 0 { 112 sporadic = append(sporadic, graph3[0]) 113 graph3 = graph3[1:] 114 } 115 } 116 if len(sporadic) != graph1Size+graph2Size+graph3Size { 117 t.Error("sporadic block creation failed") 118 } 119 // Result of findSets should match previous result. 120 sets = findSets(sporadic) 121 if len(sets) != 3 { 122 t.Fatal("there should be two sets") 123 } 124 lens = []int{len(sets[0]), len(sets[1]), len(sets[2])} 125 sort.Ints(lens) 126 expected = []int{graph1Size, graph2Size, graph3Size} 127 sort.Ints(expected) 128 if lens[0] != expected[0] || lens[1] != expected[1] || lens[2] != expected[2] { 129 t.Error("Resulting sets do not have the right lengths") 130 } 131 } 132 133 // TestArbDataOnly tries submitting a transaction with only arbitrary data to 134 // the transaction pool. Then a block is mined, putting the transaction on the 135 // blockchain. The arb data transaction should no longer be in the transaction 136 // pool. 137 func TestArbDataOnly(t *testing.T) { 138 if testing.Short() { 139 t.SkipNow() 140 } 141 tpt, err := createTpoolTester(t.Name()) 142 if err != nil { 143 t.Fatal(err) 144 } 145 defer tpt.Close() 146 txn := types.Transaction{ 147 ArbitraryData: [][]byte{ 148 append(modules.PrefixNonSia[:], []byte("arb-data")...), 149 }, 150 } 151 err = tpt.tpool.AcceptTransactionSet([]types.Transaction{txn}) 152 if err != nil { 153 t.Fatal(err) 154 } 155 if len(tpt.tpool.TransactionList()) != 1 { 156 t.Error("expecting to see a transaction in the transaction pool") 157 } 158 _, err = tpt.miner.AddBlock() 159 if err != nil { 160 t.Fatal(err) 161 } 162 if len(tpt.tpool.TransactionList()) != 0 { 163 t.Error("transaction was not cleared from the transaction pool") 164 } 165 } 166 167 // TestValidRevertedTransaction verifies that if a transaction appears in a 168 // block's reverted transactions, it is added correctly to the pool. 169 func TestValidRevertedTransaction(t *testing.T) { 170 if testing.Short() { 171 t.SkipNow() 172 } 173 tpt, err := createTpoolTester(t.Name()) 174 if err != nil { 175 t.Fatal(err) 176 } 177 defer tpt.Close() 178 // Mine a few extra blocks on tpt to get past the signature hardfork height. 179 for i := 0; i < 10; i++ { 180 _, err = tpt.miner.AddBlock() 181 if err != nil { 182 t.Fatal(err) 183 } 184 } 185 tpt2, err := blankTpoolTester(t.Name() + "-tpt2") 186 if err != nil { 187 t.Fatal(err) 188 } 189 defer tpt2.Close() 190 191 // connect the testers and wait for them to have the same current block 192 err = tpt2.gateway.Connect(tpt.gateway.Address()) 193 if err != nil { 194 t.Fatal(err) 195 } 196 success := false 197 for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(time.Millisecond * 100) { 198 if tpt.cs.CurrentBlock().ID() == tpt2.cs.CurrentBlock().ID() { 199 success = true 200 break 201 } 202 } 203 if !success { 204 t.Fatal("testers did not have the same block height after one minute") 205 } 206 207 // disconnect the testers 208 err = tpt2.gateway.Disconnect(tpt.gateway.Address()) 209 if err != nil { 210 t.Fatal(err) 211 } 212 tpt.gateway.Disconnect(tpt2.gateway.Address()) 213 214 // make some transactions on tpt 215 var txnSets [][]types.Transaction 216 for i := 0; i < 5; i++ { 217 txns, err := tpt.wallet.SendSiacoins(types.SiacoinPrecision.Mul64(1000), types.UnlockHash{}) 218 if err != nil { 219 t.Fatal(err) 220 } 221 txnSets = append(txnSets, txns) 222 } 223 // mine some blocks to cause a re-org 224 for i := 0; i < 3; i++ { 225 _, err = tpt.miner.AddBlock() 226 if err != nil { 227 t.Fatal(err) 228 } 229 } 230 // put tpt2 at a higher height 231 for i := 0; i < 10; i++ { 232 _, err = tpt2.miner.AddBlock() 233 if err != nil { 234 t.Fatal(err) 235 } 236 } 237 238 // connect the testers and wait for them to have the same current block 239 err = tpt.gateway.Connect(tpt2.gateway.Address()) 240 if err != nil { 241 t.Fatal(err) 242 } 243 success = false 244 for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(time.Millisecond * 100) { 245 if tpt.cs.CurrentBlock().ID() == tpt2.cs.CurrentBlock().ID() { 246 success = true 247 break 248 } 249 } 250 if !success { 251 t.Fatal("testers did not have the same block height after one minute") 252 } 253 254 // verify the transaction pool still has the reorged txns 255 for _, txnSet := range txnSets { 256 for _, txn := range txnSet { 257 _, _, exists := tpt.tpool.Transaction(txn.ID()) 258 if !exists { 259 t.Error("Transaction was not re-added to the transaction pool after being re-orged out of the blockchain:", txn.ID()) 260 } 261 } 262 } 263 264 // Try to get the transactoins into a block. 265 _, err = tpt.miner.AddBlock() 266 if err != nil { 267 t.Fatal(err) 268 } 269 if len(tpt.tpool.TransactionList()) != 0 { 270 t.Error("Does not seem that the transactions were added to the transaction pool.") 271 } 272 } 273 274 // TestTransactionPoolPruning verifies that the transaction pool correctly 275 // prunes transactions older than maxTxnAge. 276 func TestTransactionPoolPruning(t *testing.T) { 277 if testing.Short() { 278 t.SkipNow() 279 } 280 281 tpt, err := createTpoolTester(t.Name()) 282 if err != nil { 283 t.Fatal(err) 284 } 285 defer tpt.Close() 286 tpt2, err := blankTpoolTester(t.Name() + "-tpt2") 287 if err != nil { 288 t.Fatal(err) 289 } 290 defer tpt2.Close() 291 292 // connect the testers and wait for them to have the same current block 293 err = tpt2.gateway.Connect(tpt.gateway.Address()) 294 if err != nil { 295 t.Fatal(err) 296 } 297 success := false 298 for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(time.Millisecond * 100) { 299 if tpt.cs.CurrentBlock().ID() == tpt2.cs.CurrentBlock().ID() { 300 success = true 301 break 302 } 303 } 304 if !success { 305 t.Fatal("testers did not have the same block height after one minute") 306 } 307 308 // disconnect tpt, create an unconfirmed transaction on tpt, mine maxTxnAge 309 // blocks on tpt2 and reconnect. The unconfirmed transactions should be 310 // removed from tpt's pool. 311 err = tpt.gateway.Disconnect(tpt2.gateway.Address()) 312 if err != nil { 313 t.Fatal(err) 314 } 315 tpt2.gateway.Disconnect(tpt.gateway.Address()) 316 txns, err := tpt.wallet.SendSiacoins(types.SiacoinPrecision.Mul64(1000), types.UnlockHash{}) 317 if err != nil { 318 t.Fatal(err) 319 } 320 for i := types.BlockHeight(0); i < maxTxnAge+1; i++ { 321 _, err = tpt2.miner.AddBlock() 322 if err != nil { 323 t.Fatal(err) 324 } 325 } 326 327 // reconnect the testers 328 err = tpt.gateway.Connect(tpt2.gateway.Address()) 329 if err != nil { 330 t.Fatal(err) 331 } 332 success = false 333 for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(time.Millisecond * 100) { 334 if tpt.cs.CurrentBlock().ID() == tpt2.cs.CurrentBlock().ID() { 335 success = true 336 break 337 } 338 } 339 if !success { 340 t.Fatal("testers did not have the same block height after one minute") 341 } 342 343 for _, txn := range txns { 344 _, _, exists := tpt.tpool.Transaction(txn.ID()) 345 if exists { 346 t.Fatal("transaction pool had a transaction that should have been pruned") 347 } 348 } 349 if len(tpt.tpool.TransactionList()) != 0 { 350 t.Fatal("should have no unconfirmed transactions") 351 } 352 if len(tpt.tpool.knownObjects) != 0 { 353 t.Fatal("should have no known objects") 354 } 355 if len(tpt.tpool.transactionSetDiffs) != 0 { 356 t.Fatal("should have no transaction set diffs") 357 } 358 if tpt.tpool.transactionListSize != 0 { 359 t.Fatal("transactionListSize should be zero") 360 } 361 } 362 363 // TestUpdateBlockHeight verifies that the transactionpool updates its internal 364 // block height correctly. 365 func TestUpdateBlockHeight(t *testing.T) { 366 if testing.Short() { 367 t.SkipNow() 368 } 369 370 tpt, err := blankTpoolTester(t.Name()) 371 if err != nil { 372 t.Fatal(err) 373 } 374 defer tpt.Close() 375 376 targetHeight := 20 377 for i := 0; i < targetHeight; i++ { 378 _, err = tpt.miner.AddBlock() 379 if err != nil { 380 t.Fatal(err) 381 } 382 } 383 if tpt.tpool.blockHeight != types.BlockHeight(targetHeight) { 384 t.Fatalf("transaction pool had the wrong block height, got %v wanted %v\n", tpt.tpool.blockHeight, targetHeight) 385 } 386 } 387 388 // TestDatabaseUpgrade verifies that the database will upgrade correctly from 389 // v1.3.1 or earlier to the new sanity check persistence, by clearing out the 390 // persistence at various points in the process of a reorg. 391 func TestDatabaseUpgrade(t *testing.T) { 392 if testing.Short() { 393 t.SkipNow() 394 } 395 tpt, err := createTpoolTester(t.Name()) 396 if err != nil { 397 t.Fatal(err) 398 } 399 defer tpt.Close() 400 tpt2, err := blankTpoolTester(t.Name() + "-tpt2") 401 if err != nil { 402 t.Fatal(err) 403 } 404 defer tpt2.Close() 405 406 // connect the testers and wait for them to have the same current block 407 err = tpt2.gateway.Connect(tpt.gateway.Address()) 408 if err != nil { 409 t.Fatal(err) 410 } 411 success := false 412 for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(time.Millisecond * 100) { 413 if tpt.cs.CurrentBlock().ID() == tpt2.cs.CurrentBlock().ID() { 414 success = true 415 break 416 } 417 } 418 if !success { 419 t.Fatal("testers did not have the same block height after one minute") 420 } 421 422 // disconnect the testers 423 err = tpt2.gateway.Disconnect(tpt.gateway.Address()) 424 if err != nil { 425 t.Fatal(err) 426 } 427 tpt.gateway.Disconnect(tpt2.gateway.Address()) 428 429 // make some transactions on tpt 430 var txnSets [][]types.Transaction 431 for i := 0; i < 5; i++ { 432 txns, err := tpt.wallet.SendSiacoins(types.SiacoinPrecision.Mul64(1000), types.UnlockHash{}) 433 if err != nil { 434 t.Fatal(err) 435 } 436 txnSets = append(txnSets, txns) 437 } 438 // mine some blocks to cause a re-org, first clearing the persistence to 439 // simulate an un-upgraded database. 440 err = tpt.tpool.dbTx.Bucket(bucketRecentConsensusChange).Delete(fieldRecentBlockID) 441 if err != nil { 442 t.Fatal(err) 443 } 444 for i := 0; i < 3; i++ { 445 _, err = tpt.miner.AddBlock() 446 if err != nil { 447 t.Fatal(err) 448 } 449 } 450 // put tpt2 at a higher height 451 for i := 0; i < 10; i++ { 452 _, err = tpt2.miner.AddBlock() 453 if err != nil { 454 t.Fatal(err) 455 } 456 } 457 458 // connect the testers and wait for them to have the same current block, 459 // first clearing the persistence to simulate an un-upgraded database. 460 err = tpt.tpool.dbTx.Bucket(bucketRecentConsensusChange).Delete(fieldRecentBlockID) 461 if err != nil { 462 t.Fatal(err) 463 } 464 err = tpt.gateway.Connect(tpt2.gateway.Address()) 465 if err != nil { 466 t.Fatal(err) 467 } 468 success = false 469 for start := time.Now(); time.Since(start) < time.Minute; time.Sleep(time.Millisecond * 100) { 470 if tpt.cs.CurrentBlock().ID() == tpt2.cs.CurrentBlock().ID() { 471 success = true 472 break 473 } 474 } 475 if !success { 476 t.Fatal("testers did not have the same block height after one minute") 477 } 478 }