github.com/NebulousLabs/Sia@v1.3.7/modules/consensus/maintenance_test.go (about) 1 package consensus 2 3 /* 4 import ( 5 "testing" 6 7 "github.com/coreos/bbolt" 8 9 "github.com/NebulousLabs/Sia/modules" 10 "github.com/NebulousLabs/Sia/types" 11 ) 12 13 // TestApplyMinerPayouts probes the applyMinerPayouts method of the consensus 14 // set. 15 func TestApplyMinerPayouts(t *testing.T) { 16 if testing.Short() { 17 t.SkipNow() 18 } 19 cst, err := createConsensusSetTester(t.Name()) 20 if err != nil { 21 t.Fatal(err) 22 } 23 defer cst.closeCst() 24 25 // Create a block node with a single miner payout. 26 pb := new(processedBlock) 27 pb.Height = cst.cs.dbBlockHeight() 28 pb.Block.Timestamp = 2 // MinerPayout id is determined by block id + index; add uniqueness to the block id. 29 pb.Block.MinerPayouts = append(pb.Block.MinerPayouts, types.SiacoinOutput{Value: types.NewCurrency64(12)}) 30 mpid0 := pb.Block.MinerPayoutID(0) 31 32 // Apply the single miner payout. 33 _ = cst.cs.db.Update(func(tx *bolt.Tx) error { 34 applyMinerPayouts(tx, pb) 35 return nil 36 }) 37 exists := cst.cs.db.inDelayedSiacoinOutputsHeight(cst.cs.dbBlockHeight()+types.MaturityDelay, mpid0) 38 if !exists { 39 t.Error("miner payout was not created in the delayed outputs set") 40 } 41 dsco, err := cst.cs.dbGetDSCO(cst.cs.dbBlockHeight()+types.MaturityDelay, mpid0) 42 if err != nil { 43 t.Fatal(err) 44 } 45 if dsco.Value.Cmp64(12) != 0 { 46 t.Error("miner payout created with wrong currency value") 47 } 48 exists = cst.cs.db.inSiacoinOutputs(mpid0) 49 if exists { 50 t.Error("miner payout was added to the siacoin output set") 51 } 52 if cst.cs.db.lenDelayedSiacoinOutputsHeight(cst.cs.dbBlockHeight()+types.MaturityDelay) != 2 { // 1 for consensus set creation, 1 for the output that just got added. 53 t.Error("wrong number of delayed siacoin outputs in consensus set") 54 } 55 if len(pb.DelayedSiacoinOutputDiffs) != 1 { 56 t.Fatal("block node did not get the delayed siacoin output diff") 57 } 58 if pb.DelayedSiacoinOutputDiffs[0].Direction != modules.DiffApply { 59 t.Error("delayed siacoin output diff has the wrong direction") 60 } 61 if pb.DelayedSiacoinOutputDiffs[0].ID != mpid0 { 62 t.Error("delayed siacoin output diff has wrong id") 63 } 64 65 // Apply a processed block with two miner payouts. 66 pb2 := new(processedBlock) 67 pb2.Height = cst.cs.dbBlockHeight() 68 pb2.Block.Timestamp = 5 // MinerPayout id is determined by block id + index; add uniqueness to the block id. 69 pb2.Block.MinerPayouts = []types.SiacoinOutput{ 70 {Value: types.NewCurrency64(5)}, 71 {Value: types.NewCurrency64(10)}, 72 } 73 mpid1 := pb2.Block.MinerPayoutID(0) 74 mpid2 := pb2.Block.MinerPayoutID(1) 75 _ = cst.cs.db.Update(func(tx *bolt.Tx) error { 76 applyMinerPayouts(tx, pb2) 77 return nil 78 }) 79 exists = cst.cs.db.inDelayedSiacoinOutputsHeight(cst.cs.dbBlockHeight()+types.MaturityDelay, mpid1) 80 if !exists { 81 t.Error("delayed siacoin output was not created") 82 } 83 exists = cst.cs.db.inDelayedSiacoinOutputsHeight(cst.cs.dbBlockHeight()+types.MaturityDelay, mpid2) 84 if !exists { 85 t.Error("delayed siacoin output was not created") 86 } 87 if len(pb2.DelayedSiacoinOutputDiffs) != 2 { 88 t.Error("block node should have 2 delayed outputs") 89 } 90 91 // Trigger a panic where the miner payouts have already been applied. 92 defer func() { 93 r := recover() 94 if r == nil { 95 t.Error("expecting error after corrupting database") 96 } 97 }() 98 defer func() { 99 r := recover() 100 if r == nil { 101 t.Error("expecting error after corrupting database") 102 } 103 cst.cs.db.rmDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, mpid0) 104 cst.cs.db.addSiacoinOutputs(mpid0, types.SiacoinOutput{}) 105 _ = cst.cs.db.Update(func(tx *bolt.Tx) error { 106 applyMinerPayouts(tx, pb) 107 return nil 108 }) 109 }() 110 _ = cst.cs.db.Update(func(tx *bolt.Tx) error { 111 applyMinerPayouts(tx, pb) 112 return nil 113 }) 114 } 115 116 // TestApplyMaturedSiacoinOutputs probes the applyMaturedSiacoinOutputs method 117 // of the consensus set. 118 func TestApplyMaturedSiacoinOutputs(t *testing.T) { 119 if testing.Short() { 120 t.SkipNow() 121 } 122 cst, err := createConsensusSetTester(t.Name()) 123 if err != nil { 124 t.Fatal(err) 125 } 126 defer cst.closeCst() 127 pb := cst.cs.dbCurrentProcessedBlock() 128 129 // Trigger the sanity check concerning already-matured outputs. 130 defer func() { 131 r := recover() 132 if r != errOutputAlreadyMature { 133 t.Error(r) 134 } 135 }() 136 cst.cs.db.addSiacoinOutputs(types.SiacoinOutputID{}, types.SiacoinOutput{}) 137 _ = cst.cs.db.Update(func(tx *bolt.Tx) error { 138 createDSCOBucket(tx, pb.Height) 139 return nil 140 }) 141 cst.cs.db.addDelayedSiacoinOutputsHeight(pb.Height, types.SiacoinOutputID{}, types.SiacoinOutput{}) 142 _ = cst.cs.db.Update(func(tx *bolt.Tx) error { 143 applyMaturedSiacoinOutputs(tx, pb) 144 return nil 145 }) 146 } 147 148 // TestApplyMissedStorageProof probes the applyMissedStorageProof method of the 149 // consensus set. 150 func TestApplyMissedStorageProof(t *testing.T) { 151 if testing.Short() { 152 t.SkipNow() 153 } 154 cst, err := createConsensusSetTester(t.Name()) 155 if err != nil { 156 t.Fatal(err) 157 } 158 defer cst.closeCst() 159 160 // Create a block node. 161 pb := new(processedBlock) 162 pb.Height = cst.cs.height() 163 164 // Create a file contract that's expiring and has 1 missed proof output. 165 expiringFC := types.FileContract{ 166 Payout: types.NewCurrency64(300e3), 167 WindowEnd: pb.Height, 168 MissedProofOutputs: []types.SiacoinOutput{{Value: types.NewCurrency64(290e3)}}, 169 } 170 // Assign the contract a 0-id. 171 cst.cs.db.addFileContracts(types.FileContractID{}, expiringFC) 172 cst.cs.db.addFCExpirations(pb.Height) 173 cst.cs.db.addFCExpirationsHeight(pb.Height, types.FileContractID{}) 174 cst.cs.applyMissedStorageProof(pb, types.FileContractID{}) 175 exists := cst.cs.db.inFileContracts(types.FileContractID{}) 176 if exists { 177 t.Error("file contract was not consumed in missed storage proof") 178 } 179 spoid := types.FileContractID{}.StorageProofOutputID(types.ProofMissed, 0) 180 exists = cst.cs.db.inDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid) 181 if !exists { 182 t.Error("missed proof output was never created") 183 } 184 exists = cst.cs.db.inSiacoinOutputs(spoid) 185 if exists { 186 t.Error("storage proof output made it into the siacoin output set") 187 } 188 exists = cst.cs.db.inFileContracts(types.FileContractID{}) 189 if exists { 190 t.Error("file contract remains after expiration") 191 } 192 193 // Trigger the debug panics. 194 // not exist. 195 defer func() { 196 r := recover() 197 if r != errNilItem { 198 t.Error(r) 199 } 200 }() 201 defer func() { 202 r := recover() 203 if r != errNilItem { 204 t.Error(r) 205 } 206 // Trigger errMissingFileContract 207 cst.cs.applyMissedStorageProof(pb, types.FileContractID(spoid)) 208 }() 209 defer func() { 210 r := recover() 211 if r != errNilItem { 212 t.Error(r) 213 } 214 215 // Trigger errStorageProofTiming 216 expiringFC.WindowEnd = 0 217 cst.cs.applyMissedStorageProof(pb, types.FileContractID{}) 218 }() 219 defer func() { 220 r := recover() 221 if r != errNilItem { 222 t.Error(r) 223 } 224 225 // Trigger errPayoutsAlreadyPaid from siacoin outputs. 226 cst.cs.db.rmDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid) 227 cst.cs.db.addSiacoinOutputs(spoid, types.SiacoinOutput{}) 228 cst.cs.applyMissedStorageProof(pb, types.FileContractID{}) 229 }() 230 // Trigger errPayoutsAlreadyPaid from delayed outputs. 231 cst.cs.db.rmFileContracts(types.FileContractID{}) 232 cst.cs.db.addFileContracts(types.FileContractID{}, expiringFC) 233 cst.cs.db.addDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid, types.SiacoinOutput{}) 234 cst.cs.applyMissedStorageProof(pb, types.FileContractID{}) 235 } 236 */ 237 238 // TestApplyFileContractMaintenance probes the applyFileContractMaintenance 239 // method of the consensus set. 240 /* 241 func TestApplyFileContractMaintenance(t *testing.T) { 242 if testing.Short() { 243 t.SkipNow() 244 } 245 cst, err := createConsensusSetTester(t.Name()) 246 if err != nil { 247 t.Fatal(err) 248 } 249 defer cst.closeCst() 250 251 // Create a block node. 252 pb := new(processedBlock) 253 pb.Height = cst.cs.height() 254 255 // Create a file contract that's expiring and has 1 missed proof output. 256 expiringFC := types.FileContract{ 257 Payout: types.NewCurrency64(300e3), 258 WindowEnd: pb.Height, 259 MissedProofOutputs: []types.SiacoinOutput{{Value: types.NewCurrency64(290e3)}}, 260 } 261 // Assign the contract a 0-id. 262 cst.cs.db.addFileContracts(types.FileContractID{}, expiringFC) 263 cst.cs.db.addFCExpirations(pb.Height) 264 cst.cs.db.addFCExpirationsHeight(pb.Height, types.FileContractID{}) 265 err = cst.cs.db.Update(func(tx *bolt.Tx) error { 266 applyFileContractMaintenance(tx, pb) 267 return nil 268 }) 269 if err != nil { 270 t.Fatal(err) 271 } 272 exists := cst.cs.db.inFileContracts(types.FileContractID{}) 273 if exists { 274 t.Error("file contract was not consumed in missed storage proof") 275 } 276 spoid := types.FileContractID{}.StorageProofOutputID(types.ProofMissed, 0) 277 exists = cst.cs.db.inDelayedSiacoinOutputsHeight(pb.Height+types.MaturityDelay, spoid) 278 if !exists { 279 t.Error("missed proof output was never created") 280 } 281 exists = cst.cs.db.inSiacoinOutputs(spoid) 282 if exists { 283 t.Error("storage proof output made it into the siacoin output set") 284 } 285 exists = cst.cs.db.inFileContracts(types.FileContractID{}) 286 if exists { 287 t.Error("file contract remains after expiration") 288 } 289 } 290 */