github.com/NebulousLabs/Sia@v1.3.7/modules/consensus/maintenance.go (about) 1 package consensus 2 3 import ( 4 "errors" 5 6 "github.com/NebulousLabs/Sia/build" 7 "github.com/NebulousLabs/Sia/encoding" 8 "github.com/NebulousLabs/Sia/modules" 9 "github.com/NebulousLabs/Sia/types" 10 11 "github.com/coreos/bbolt" 12 ) 13 14 var ( 15 errMissingFileContract = errors.New("storage proof submitted for non existing file contract") 16 errOutputAlreadyMature = errors.New("delayed siacoin output is already in the matured outputs set") 17 errPayoutsAlreadyPaid = errors.New("payouts are already in the consensus set") 18 errStorageProofTiming = errors.New("missed proof triggered for file contract that is not expiring") 19 ) 20 21 // applyMinerPayouts adds a block's miner payouts to the consensus set as 22 // delayed siacoin outputs. 23 func applyMinerPayouts(tx *bolt.Tx, pb *processedBlock) { 24 for i := range pb.Block.MinerPayouts { 25 mpid := pb.Block.MinerPayoutID(uint64(i)) 26 dscod := modules.DelayedSiacoinOutputDiff{ 27 Direction: modules.DiffApply, 28 ID: mpid, 29 SiacoinOutput: pb.Block.MinerPayouts[i], 30 MaturityHeight: pb.Height + types.MaturityDelay, 31 } 32 pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) 33 commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply) 34 } 35 } 36 37 // applyMaturedSiacoinOutputs goes through the list of siacoin outputs that 38 // have matured and adds them to the consensus set. This also updates the block 39 // node diff set. 40 func applyMaturedSiacoinOutputs(tx *bolt.Tx, pb *processedBlock) { 41 // Skip this step if the blockchain is not old enough to have maturing 42 // outputs. 43 if pb.Height < types.MaturityDelay { 44 return 45 } 46 47 // Iterate through the list of delayed siacoin outputs. Sometimes boltdb 48 // has trouble if you delete elements in a bucket while iterating through 49 // the bucket (and sometimes not - nondeterministic), so all of the 50 // elements are collected into an array and then deleted after the bucket 51 // scan is complete. 52 bucketID := append(prefixDSCO, encoding.Marshal(pb.Height)...) 53 var scods []modules.SiacoinOutputDiff 54 var dscods []modules.DelayedSiacoinOutputDiff 55 dbErr := tx.Bucket(bucketID).ForEach(func(idBytes, scoBytes []byte) error { 56 // Decode the key-value pair into an id and a siacoin output. 57 var id types.SiacoinOutputID 58 var sco types.SiacoinOutput 59 copy(id[:], idBytes) 60 encErr := encoding.Unmarshal(scoBytes, &sco) 61 if build.DEBUG && encErr != nil { 62 panic(encErr) 63 } 64 65 // Sanity check - the output should not already be in siacoinOuptuts. 66 if build.DEBUG && isSiacoinOutput(tx, id) { 67 panic(errOutputAlreadyMature) 68 } 69 70 // Add the output to the ConsensusSet and record the diff in the 71 // blockNode. 72 scod := modules.SiacoinOutputDiff{ 73 Direction: modules.DiffApply, 74 ID: id, 75 SiacoinOutput: sco, 76 } 77 scods = append(scods, scod) 78 79 // Create the dscod and add it to the list of dscods that should be 80 // deleted. 81 dscod := modules.DelayedSiacoinOutputDiff{ 82 Direction: modules.DiffRevert, 83 ID: id, 84 SiacoinOutput: sco, 85 MaturityHeight: pb.Height, 86 } 87 dscods = append(dscods, dscod) 88 return nil 89 }) 90 if build.DEBUG && dbErr != nil { 91 panic(dbErr) 92 } 93 for _, scod := range scods { 94 pb.SiacoinOutputDiffs = append(pb.SiacoinOutputDiffs, scod) 95 commitSiacoinOutputDiff(tx, scod, modules.DiffApply) 96 } 97 for _, dscod := range dscods { 98 pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) 99 commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply) 100 } 101 deleteDSCOBucket(tx, pb.Height) 102 } 103 104 // applyMissedStorageProof adds the outputs and diffs that result from a file 105 // contract expiring. 106 func applyMissedStorageProof(tx *bolt.Tx, pb *processedBlock, fcid types.FileContractID) (dscods []modules.DelayedSiacoinOutputDiff, fcd modules.FileContractDiff) { 107 // Sanity checks. 108 fc, err := getFileContract(tx, fcid) 109 if build.DEBUG && err != nil { 110 panic(err) 111 } 112 if build.DEBUG { 113 // Check that the file contract in question expires at pb.Height. 114 if fc.WindowEnd != pb.Height { 115 panic(errStorageProofTiming) 116 } 117 } 118 119 // Add all of the outputs in the missed proof outputs to the consensus set. 120 for i, mpo := range fc.MissedProofOutputs { 121 // Sanity check - output should not already exist. 122 spoid := fcid.StorageProofOutputID(types.ProofMissed, uint64(i)) 123 if build.DEBUG && isSiacoinOutput(tx, spoid) { 124 panic(errPayoutsAlreadyPaid) 125 } 126 127 // Don't add the output if the value is zero. 128 dscod := modules.DelayedSiacoinOutputDiff{ 129 Direction: modules.DiffApply, 130 ID: spoid, 131 SiacoinOutput: mpo, 132 MaturityHeight: pb.Height + types.MaturityDelay, 133 } 134 dscods = append(dscods, dscod) 135 } 136 137 // Remove the file contract from the consensus set and record the diff in 138 // the blockNode. 139 fcd = modules.FileContractDiff{ 140 Direction: modules.DiffRevert, 141 ID: fcid, 142 FileContract: fc, 143 } 144 return dscods, fcd 145 } 146 147 // applyFileContractMaintenance looks for all of the file contracts that have 148 // expired without an appropriate storage proof, and calls 'applyMissedProof' 149 // for the file contract. 150 func applyFileContractMaintenance(tx *bolt.Tx, pb *processedBlock) { 151 // Get the bucket pointing to all of the expiring file contracts. 152 fceBucketID := append(prefixFCEX, encoding.Marshal(pb.Height)...) 153 fceBucket := tx.Bucket(fceBucketID) 154 // Finish if there are no expiring file contracts. 155 if fceBucket == nil { 156 return 157 } 158 159 var dscods []modules.DelayedSiacoinOutputDiff 160 var fcds []modules.FileContractDiff 161 err := fceBucket.ForEach(func(keyBytes, valBytes []byte) error { 162 var id types.FileContractID 163 copy(id[:], keyBytes) 164 amspDSCODS, fcd := applyMissedStorageProof(tx, pb, id) 165 fcds = append(fcds, fcd) 166 dscods = append(dscods, amspDSCODS...) 167 return nil 168 }) 169 if build.DEBUG && err != nil { 170 panic(err) 171 } 172 for _, dscod := range dscods { 173 pb.DelayedSiacoinOutputDiffs = append(pb.DelayedSiacoinOutputDiffs, dscod) 174 commitDelayedSiacoinOutputDiff(tx, dscod, modules.DiffApply) 175 } 176 for _, fcd := range fcds { 177 pb.FileContractDiffs = append(pb.FileContractDiffs, fcd) 178 commitFileContractDiff(tx, fcd, modules.DiffApply) 179 } 180 err = tx.DeleteBucket(fceBucketID) 181 if build.DEBUG && err != nil { 182 panic(err) 183 } 184 } 185 186 // applyMaintenance applies block-level alterations to the consensus set. 187 // Maintenance is applied after all of the transactions for the block have been 188 // applied. 189 func applyMaintenance(tx *bolt.Tx, pb *processedBlock) { 190 applyMinerPayouts(tx, pb) 191 applyMaturedSiacoinOutputs(tx, pb) 192 applyFileContractMaintenance(tx, pb) 193 }