github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/consensus/accept_reorg_test.go (about) 1 package consensus 2 3 import ( 4 "testing" 5 6 "github.com/NebulousLabs/Sia/modules" 7 "github.com/NebulousLabs/Sia/types" 8 ) 9 10 // reorgSets contains multiple consensus sets that share a genesis block, which 11 // can be manipulated to cause full integration blockchain reorgs. 12 // 13 // cstBackup is a holding place for cstMain - the blocks originally in cstMain get moved 14 // to cstBackup so that cstMain can be reorganized without that history being lost. 15 // Extending cstBackup will allow cstMain to be reorg'd back to its original blocks. 16 type reorgSets struct { 17 cstMain *consensusSetTester 18 cstAlt *consensusSetTester 19 cstBackup *consensusSetTester 20 } 21 22 // Close will close all of the testers in the reorgSets. Because we don't yet 23 // have a good way to check errors on a deferred statement, a panic will be 24 // thrown if there are any problems closing the reorgSets. 25 func (rs *reorgSets) Close() error { 26 err := rs.cstMain.Close() 27 if err != nil { 28 panic(err) 29 } 30 err = rs.cstAlt.Close() 31 if err != nil { 32 panic(err) 33 } 34 err = rs.cstBackup.Close() 35 if err != nil { 36 panic(err) 37 } 38 return nil 39 } 40 41 // createReorgSets creates a reorg set that is ready to be manipulated. 42 func createReorgSets(name string) *reorgSets { 43 cstMain, err := createConsensusSetTester(name + " - 1") 44 if err != nil { 45 panic(err) 46 } 47 cstAlt, err := createConsensusSetTester(name + " - 2") 48 if err != nil { 49 panic(err) 50 } 51 cstBackup, err := createConsensusSetTester(name + " - 3") 52 if err != nil { 53 panic(err) 54 } 55 56 return &reorgSets{ 57 cstMain: cstMain, 58 cstAlt: cstAlt, 59 cstBackup: cstBackup, 60 } 61 } 62 63 // save takes all of the blocks in cstMain and moves them to cstBackup. 64 func (rs *reorgSets) save() { 65 mainHeight := rs.cstMain.cs.dbBlockHeight() 66 for i := types.BlockHeight(1); i <= mainHeight; i++ { 67 id, err := rs.cstMain.cs.dbGetPath(i) 68 if err != nil { 69 panic(err) 70 } 71 pb, err := rs.cstMain.cs.dbGetBlockMap(id) 72 if err != nil { 73 panic(err) 74 } 75 76 // err is not checked - block may already be in cstBackup. 77 _ = rs.cstBackup.cs.AcceptBlock(pb.Block) 78 } 79 80 // Check that cstMain and cstBackup are even. 81 if rs.cstMain.cs.dbCurrentProcessedBlock().Block.ID() != rs.cstBackup.cs.dbCurrentProcessedBlock().Block.ID() { 82 panic("could not save cstMain into cstBackup") 83 } 84 if rs.cstMain.cs.dbConsensusChecksum() != rs.cstBackup.cs.dbConsensusChecksum() { 85 panic("reorg checksums do not match after saving") 86 } 87 } 88 89 // extend adds blocks to cstAlt until cstAlt has more weight than cstMain. Then 90 // cstMain is caught up, causing cstMain to perform a reorg that extends all 91 // the way to the genesis block. 92 func (rs *reorgSets) extend() { 93 for rs.cstMain.cs.dbBlockHeight() >= rs.cstAlt.cs.dbBlockHeight() { 94 _, err := rs.cstAlt.miner.AddBlock() 95 if err != nil { 96 panic(err) 97 } 98 } 99 for i := types.BlockHeight(1); i <= rs.cstAlt.cs.dbBlockHeight(); i++ { 100 id, err := rs.cstAlt.cs.dbGetPath(i) 101 if err != nil { 102 panic(err) 103 } 104 pb, err := rs.cstAlt.cs.dbGetBlockMap(id) 105 if err != nil { 106 panic(err) 107 } 108 _ = rs.cstMain.cs.AcceptBlock(pb.Block) 109 } 110 111 // Check that cstMain and cstAlt are even. 112 if rs.cstMain.cs.dbCurrentProcessedBlock().Block.ID() != rs.cstAlt.cs.dbCurrentProcessedBlock().Block.ID() { 113 panic("could not save cstMain into cstAlt") 114 } 115 if rs.cstMain.cs.dbConsensusChecksum() != rs.cstAlt.cs.dbConsensusChecksum() { 116 panic("reorg checksums do not match after extending") 117 } 118 } 119 120 // restore extends cstBackup until it is ahead of cstMain, and then adds all of 121 // the blocks from cstBackup to cstMain, causing cstMain to reorg to the state 122 // of cstBackup. 123 func (rs *reorgSets) restore() { 124 for rs.cstMain.cs.dbBlockHeight() >= rs.cstBackup.cs.dbBlockHeight() { 125 _, err := rs.cstBackup.miner.AddBlock() 126 if err != nil { 127 panic(err) 128 } 129 } 130 for i := types.BlockHeight(1); i <= rs.cstBackup.cs.dbBlockHeight(); i++ { 131 id, err := rs.cstBackup.cs.dbGetPath(i) 132 if err != nil { 133 panic(err) 134 } 135 pb, err := rs.cstBackup.cs.dbGetBlockMap(id) 136 if err != nil { 137 panic(err) 138 } 139 _ = rs.cstMain.cs.AcceptBlock(pb.Block) 140 } 141 142 // Check that cstMain and cstBackup are even. 143 if rs.cstMain.cs.dbCurrentProcessedBlock().Block.ID() != rs.cstBackup.cs.dbCurrentProcessedBlock().Block.ID() { 144 panic("could not save cstMain into cstBackup") 145 } 146 if rs.cstMain.cs.dbConsensusChecksum() != rs.cstBackup.cs.dbConsensusChecksum() { 147 panic("reorg checksums do not match after restoring") 148 } 149 } 150 151 // fullReorg saves all of the blocks from cstMain into cstBackup, then extends 152 // cstAlt until cstMain joins cstAlt in structure. Then cstBackup is extended 153 // and cstMain is reorg'd back to have all of the original blocks. 154 func (rs *reorgSets) fullReorg() { 155 rs.save() 156 rs.extend() 157 rs.restore() 158 } 159 160 // TestIntegrationSimpleReorg tries to reorganize a simple block out of, and 161 // then back into, the consensus set. 162 func TestIntegrationSimpleReorg(t *testing.T) { 163 if testing.Short() { 164 t.SkipNow() 165 } 166 rs := createReorgSets("TestIntegrationSimpleReorg") 167 defer rs.Close() 168 169 // Give a simple block to cstMain. 170 rs.cstMain.testSimpleBlock() 171 172 // Try to trigger consensus inconsistencies by doing a full reorg on the 173 // simple block. 174 rs.fullReorg() 175 } 176 177 // TestIntegrationSiacoinReorg tries to reorganize a siacoin output block out 178 // of, and then back into, the consensus set. 179 func TestIntegrationSiacoinReorg(t *testing.T) { 180 if testing.Short() { 181 t.SkipNow() 182 } 183 rs := createReorgSets("TestIntegrationSiacoinReorg") 184 defer rs.Close() 185 186 // Give a siacoin block to cstMain. 187 rs.cstMain.testSpendSiacoinsBlock() 188 189 // Try to trigger consensus inconsistencies by doing a full reorg on the 190 // simple block. 191 rs.fullReorg() 192 } 193 194 // TestIntegrationValidStorageProofReorg tries to reorganize a valid storage 195 // proof block out of, and then back into, the consensus set. 196 func TestIntegrationValidStorageProofReorg(t *testing.T) { 197 if testing.Short() { 198 t.SkipNow() 199 } 200 rs := createReorgSets("TestIntegrationValidStorageProofReorg") 201 defer rs.Close() 202 203 // Give a series of blocks containing a file contract and a valid storage 204 // proof to cstMain. 205 rs.cstMain.testValidStorageProofBlocks() 206 207 // Try to trigger consensus inconsistencies by doing a full reorg on the 208 // simple block. 209 rs.fullReorg() 210 } 211 212 // TestIntegrationMissedStorageProofReorg tries to reorganize a valid storage 213 // proof block out of, and then back into, the consensus set. 214 func TestIntegrationMissedStorageProofReorg(t *testing.T) { 215 if testing.Short() { 216 t.SkipNow() 217 } 218 rs := createReorgSets("TestIntegrationMissedStorageProofReorg") 219 defer rs.Close() 220 221 // Give a series of blocks containing a file contract and a valid storage 222 // proof to cstMain. 223 rs.cstMain.testMissedStorageProofBlocks() 224 225 // Try to trigger consensus inconsistencies by doing a full reorg on the 226 // simple block. 227 rs.fullReorg() 228 } 229 230 // TestIntegrationFileContractRevisionReorg tries to reorganize a valid storage 231 // proof block out of, and then back into, the consensus set. 232 func TestIntegrationFileContractRevisionReorg(t *testing.T) { 233 if testing.Short() { 234 t.SkipNow() 235 } 236 rs := createReorgSets("TestIntegrationFileContractRevisionReorg") 237 defer rs.Close() 238 239 // Give a series of blocks containing a file contract and a valid storage 240 // proof to cstMain. 241 rs.cstMain.testFileContractRevision() 242 243 // Try to trigger consensus inconsistencies by doing a full reorg on the 244 // simple block. 245 rs.fullReorg() 246 } 247 248 // TestIntegrationComplexReorg stacks up blocks of all types into a single 249 // blockchain that undergoes a massive reorg as a stress test to the codebase. 250 func TestIntegrationComplexReorg(t *testing.T) { 251 if testing.Short() { 252 t.SkipNow() 253 } 254 rs := createReorgSets("TestIntegrationComplexReorg") 255 defer rs.Close() 256 257 // Give a wide variety of block types to cstMain. 258 for i := 0; i < 3; i++ { 259 rs.cstMain.testBlockSuite() 260 } 261 // Give fewer blocks to cstAlt, while still using the same variety. 262 for i := 0; i < 2; i++ { 263 rs.cstAlt.testBlockSuite() 264 } 265 266 // Try to trigger consensus inconsistencies by doing a full reorg on the 267 // simple block. 268 rs.fullReorg() 269 } 270 271 /// All functions below this point are deprecated. /// 272 273 // TestBuriedBadFork creates a block with an invalid transaction that's not on 274 // the longest fork. The consensus set will not validate that block. Then valid 275 // blocks are added on top of it to make it the longest fork. When it becomes 276 // the longest fork, all the blocks should be fully validated and thrown out 277 // because a parent is invalid. 278 func TestBuriedBadFork(t *testing.T) { 279 if testing.Short() { 280 t.SkipNow() 281 } 282 283 cst, err := createConsensusSetTester("TestBuriedBadFork") 284 if err != nil { 285 t.Fatal(err) 286 } 287 defer cst.Close() 288 pb := cst.cs.dbCurrentProcessedBlock() 289 290 // Create a bad block that builds on a parent, so that it is part of not 291 // the longest fork. 292 badBlock := types.Block{ 293 ParentID: pb.Block.ParentID, 294 Timestamp: types.CurrentTimestamp(), 295 MinerPayouts: []types.SiacoinOutput{{Value: types.CalculateCoinbase(pb.Height)}}, 296 Transactions: []types.Transaction{{ 297 SiacoinInputs: []types.SiacoinInput{{}}, // Will trigger an error on full verification but not partial verification. 298 }}, 299 } 300 parent, err := cst.cs.dbGetBlockMap(pb.Block.ParentID) 301 if err != nil { 302 t.Fatal(err) 303 } 304 badBlock, _ = cst.miner.SolveBlock(badBlock, parent.ChildTarget) 305 err = cst.cs.AcceptBlock(badBlock) 306 if err != modules.ErrNonExtendingBlock { 307 t.Fatal(err) 308 } 309 310 // Build another bock on top of the bad block that is fully valid, this 311 // will cause a fork and full validation of the bad block, both the bad 312 // block and this block should be thrown away. 313 block := types.Block{ 314 ParentID: badBlock.ID(), 315 Timestamp: types.CurrentTimestamp(), 316 MinerPayouts: []types.SiacoinOutput{{Value: types.CalculateCoinbase(pb.Height + 1)}}, 317 } 318 block, _ = cst.miner.SolveBlock(block, parent.ChildTarget) // okay because the target will not change 319 err = cst.cs.AcceptBlock(block) 320 if err == nil { 321 t.Fatal("a bad block failed to cause an error") 322 } 323 }