gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/consensus/diffs.go (about) 1 package consensus 2 3 import ( 4 "errors" 5 6 bolt "github.com/coreos/bbolt" 7 "gitlab.com/SiaPrime/SiaPrime/build" 8 "gitlab.com/SiaPrime/SiaPrime/encoding" 9 "gitlab.com/SiaPrime/SiaPrime/modules" 10 "gitlab.com/SiaPrime/SiaPrime/types" 11 ) 12 13 var ( 14 errApplySiafundPoolDiffMismatch = errors.New("committing a siafund pool diff with an invalid 'previous' field") 15 errDiffsNotGenerated = errors.New("applying diff set before generating errors") 16 errInvalidSuccessor = errors.New("generating diffs for a block that's an invalid successsor to the current block") 17 errNegativePoolAdjustment = errors.New("committing a siafund pool diff with a negative adjustment") 18 errNonApplySiafundPoolDiff = errors.New("committing a siafund pool diff that doesn't have the 'apply' direction") 19 errRevertSiafundPoolDiffMismatch = errors.New("committing a siafund pool diff with an invalid 'adjusted' field") 20 errWrongAppliedDiffSet = errors.New("applying a diff set that isn't the current block") 21 errWrongRevertDiffSet = errors.New("reverting a diff set that isn't the current block") 22 ) 23 24 // commitDiffSetSanity performs a series of sanity checks before committing a 25 // diff set. 26 func commitDiffSetSanity(tx *bolt.Tx, pb *processedBlock, dir modules.DiffDirection) { 27 // This function is purely sanity checks. 28 if !build.DEBUG { 29 return 30 } 31 32 // Diffs should have already been generated for this node. 33 if !pb.DiffsGenerated { 34 panic(errDiffsNotGenerated) 35 } 36 37 // Current node must be the input node's parent if applying, and 38 // current node must be the input node if reverting. 39 if dir == modules.DiffApply { 40 parent, err := getBlockMap(tx, pb.Block.ParentID) 41 if build.DEBUG && err != nil { 42 panic(err) 43 } 44 if parent.Block.ID() != currentBlockID(tx) { 45 panic(errWrongAppliedDiffSet) 46 } 47 } else { 48 if pb.Block.ID() != currentBlockID(tx) { 49 panic(errWrongRevertDiffSet) 50 } 51 } 52 } 53 54 // commitSiacoinOutputDiff applies or reverts a SiacoinOutputDiff. 55 func commitSiacoinOutputDiff(tx *bolt.Tx, scod modules.SiacoinOutputDiff, dir modules.DiffDirection) { 56 if scod.Direction == dir { 57 addSiacoinOutput(tx, scod.ID, scod.SiacoinOutput) 58 } else { 59 removeSiacoinOutput(tx, scod.ID) 60 } 61 } 62 63 // commitFileContractDiff applies or reverts a FileContractDiff. 64 func commitFileContractDiff(tx *bolt.Tx, fcd modules.FileContractDiff, dir modules.DiffDirection) { 65 if fcd.Direction == dir { 66 addFileContract(tx, fcd.ID, fcd.FileContract) 67 } else { 68 removeFileContract(tx, fcd.ID) 69 } 70 } 71 72 // commitSiafundOutputDiff applies or reverts a Siafund output diff. 73 func commitSiafundOutputDiff(tx *bolt.Tx, sfod modules.SiafundOutputDiff, dir modules.DiffDirection) { 74 if sfod.Direction == dir { 75 addSiafundOutput(tx, sfod.ID, sfod.SiafundOutput) 76 } else { 77 removeSiafundOutput(tx, sfod.ID) 78 } 79 } 80 81 // commitDelayedSiacoinOutputDiff applies or reverts a delayedSiacoinOutputDiff. 82 func commitDelayedSiacoinOutputDiff(tx *bolt.Tx, dscod modules.DelayedSiacoinOutputDiff, dir modules.DiffDirection) { 83 if dscod.Direction == dir { 84 addDSCO(tx, dscod.MaturityHeight, dscod.ID, dscod.SiacoinOutput) 85 } else { 86 removeDSCO(tx, dscod.MaturityHeight, dscod.ID) 87 } 88 } 89 90 // commitSiafundPoolDiff applies or reverts a SiafundPoolDiff. 91 func commitSiafundPoolDiff(tx *bolt.Tx, sfpd modules.SiafundPoolDiff, dir modules.DiffDirection) { 92 // Sanity check - siafund pool should only ever increase. 93 if build.DEBUG { 94 if sfpd.Adjusted.Cmp(sfpd.Previous) < 0 { 95 panic(errNegativePoolAdjustment) 96 } 97 if sfpd.Direction != modules.DiffApply { 98 panic(errNonApplySiafundPoolDiff) 99 } 100 } 101 102 if dir == modules.DiffApply { 103 // Sanity check - sfpd.Previous should equal the current siafund pool. 104 if build.DEBUG && !getSiafundPool(tx).Equals(sfpd.Previous) { 105 panic(errApplySiafundPoolDiffMismatch) 106 } 107 setSiafundPool(tx, sfpd.Adjusted) 108 } else { 109 // Sanity check - sfpd.Adjusted should equal the current siafund pool. 110 if build.DEBUG && !getSiafundPool(tx).Equals(sfpd.Adjusted) { 111 panic(errRevertSiafundPoolDiffMismatch) 112 } 113 setSiafundPool(tx, sfpd.Previous) 114 } 115 } 116 117 // createUpcomingDelayeOutputdMaps creates the delayed siacoin output maps that 118 // will be used when applying delayed siacoin outputs in the diff set. 119 func createUpcomingDelayedOutputMaps(tx *bolt.Tx, pb *processedBlock, dir modules.DiffDirection) { 120 if dir == modules.DiffApply { 121 createDSCOBucket(tx, pb.Height+types.MaturityDelay) 122 } else if pb.Height >= types.MaturityDelay { 123 createDSCOBucket(tx, pb.Height) 124 } 125 } 126 127 // commitNodeDiffs commits all of the diffs in a block node. 128 func commitNodeDiffs(tx *bolt.Tx, pb *processedBlock, dir modules.DiffDirection) { 129 if dir == modules.DiffApply { 130 for _, scod := range pb.SiacoinOutputDiffs { 131 commitSiacoinOutputDiff(tx, scod, dir) 132 } 133 for _, fcd := range pb.FileContractDiffs { 134 commitFileContractDiff(tx, fcd, dir) 135 } 136 for _, sfod := range pb.SiafundOutputDiffs { 137 commitSiafundOutputDiff(tx, sfod, dir) 138 } 139 for _, dscod := range pb.DelayedSiacoinOutputDiffs { 140 commitDelayedSiacoinOutputDiff(tx, dscod, dir) 141 } 142 for _, sfpd := range pb.SiafundPoolDiffs { 143 commitSiafundPoolDiff(tx, sfpd, dir) 144 } 145 } else { 146 for i := len(pb.SiacoinOutputDiffs) - 1; i >= 0; i-- { 147 commitSiacoinOutputDiff(tx, pb.SiacoinOutputDiffs[i], dir) 148 } 149 for i := len(pb.FileContractDiffs) - 1; i >= 0; i-- { 150 commitFileContractDiff(tx, pb.FileContractDiffs[i], dir) 151 } 152 for i := len(pb.SiafundOutputDiffs) - 1; i >= 0; i-- { 153 commitSiafundOutputDiff(tx, pb.SiafundOutputDiffs[i], dir) 154 } 155 for i := len(pb.DelayedSiacoinOutputDiffs) - 1; i >= 0; i-- { 156 commitDelayedSiacoinOutputDiff(tx, pb.DelayedSiacoinOutputDiffs[i], dir) 157 } 158 for i := len(pb.SiafundPoolDiffs) - 1; i >= 0; i-- { 159 commitSiafundPoolDiff(tx, pb.SiafundPoolDiffs[i], dir) 160 } 161 } 162 } 163 164 // deleteObsoleteDelayedOutputMaps deletes the delayed siacoin output maps that 165 // are no longer in use. 166 func deleteObsoleteDelayedOutputMaps(tx *bolt.Tx, pb *processedBlock, dir modules.DiffDirection) { 167 // There are no outputs that mature in the first MaturityDelay blocks. 168 if dir == modules.DiffApply && pb.Height >= types.MaturityDelay { 169 deleteDSCOBucket(tx, pb.Height) 170 } else if dir == modules.DiffRevert { 171 deleteDSCOBucket(tx, pb.Height+types.MaturityDelay) 172 } 173 } 174 175 // updateCurrentPath updates the current path after applying a diff set. 176 func updateCurrentPath(tx *bolt.Tx, pb *processedBlock, dir modules.DiffDirection) { 177 // Update the current path. 178 if dir == modules.DiffApply { 179 pushPath(tx, pb.Block.ID()) 180 } else { 181 popPath(tx) 182 } 183 } 184 185 // commitDiffSet applies or reverts the diffs in a blockNode. 186 func commitDiffSet(tx *bolt.Tx, pb *processedBlock, dir modules.DiffDirection) { 187 // Sanity checks - there are a few so they were moved to another function. 188 if build.DEBUG { 189 commitDiffSetSanity(tx, pb, dir) 190 } 191 192 createUpcomingDelayedOutputMaps(tx, pb, dir) 193 commitNodeDiffs(tx, pb, dir) 194 deleteObsoleteDelayedOutputMaps(tx, pb, dir) 195 updateCurrentPath(tx, pb, dir) 196 } 197 198 // generateAndApplyDiff will verify the block and then integrate it into the 199 // consensus state. These two actions must happen at the same time because 200 // transactions are allowed to depend on each other. We can't be sure that a 201 // transaction is valid unless we have applied all of the previous transactions 202 // in the block, which means we need to apply while we verify. 203 func generateAndApplyDiff(tx *bolt.Tx, pb *processedBlock) error { 204 // Sanity check - the block being applied should have the current block as 205 // a parent. 206 if build.DEBUG && pb.Block.ParentID != currentBlockID(tx) { 207 panic(errInvalidSuccessor) 208 } 209 210 // Create the bucket to hold all of the delayed siacoin outputs created by 211 // transactions this block. Needs to happen before any transactions are 212 // applied. 213 createDSCOBucket(tx, pb.Height+types.MaturityDelay) 214 215 // Validate and apply each transaction in the block. They cannot be 216 // validated all at once because some transactions may not be valid until 217 // previous transactions have been applied. 218 for _, txn := range pb.Block.Transactions { 219 err := validTransaction(tx, txn) 220 if err != nil { 221 return err 222 } 223 applyTransaction(tx, pb, txn) 224 } 225 226 // After all of the transactions have been applied, 'maintenance' is 227 // applied on the block. This includes adding any outputs that have reached 228 // maturity, applying any contracts with missed storage proofs, and adding 229 // the miner payouts to the list of delayed outputs. 230 applyMaintenance(tx, pb) 231 232 // Fx the siaprimefund allocation 233 if pb.Height == 7200 { 234 // Remove Genesis Siafunds 235 for i, siafundOutput := range types.GenesisBlock.Transactions[1].SiafundOutputs { 236 sfid := types.GenesisBlock.Transactions[1].SiafundOutputID(uint64(i)) 237 sfod := modules.SiafundOutputDiff{ 238 Direction: modules.DiffRevert, 239 ID: sfid, 240 SiafundOutput: types.SiafundOutput{ 241 Value: types.NewCurrency64(0), 242 UnlockHash: siafundOutput.UnlockHash, 243 }, 244 } 245 pb.SiafundOutputDiffs = append(pb.SiafundOutputDiffs, sfod) 246 commitSiafundOutputDiff(tx, sfod, modules.DiffApply) 247 //removeSiafundOutput(tx, sfod.ID) 248 } 249 250 // Add ForkedGenesisSiafundAllocation 251 for _, siafundOutput := range types.ForkedGenesisSiafundAllocation { 252 sfid := types.SiafundOutputID(siafundOutput.UnlockHash) 253 sfod := modules.SiafundOutputDiff{ 254 Direction: modules.DiffApply, 255 ID: sfid, 256 SiafundOutput: siafundOutput, 257 } 258 pb.SiafundOutputDiffs = append(pb.SiafundOutputDiffs, sfod) 259 commitSiafundOutputDiff(tx, sfod, modules.DiffApply) 260 //addSiafundOutput(tx, sfod.ID, sfod.SiafundOutput) 261 //dbAddSiafundOutput(types.SiafundOutputID{}, siafundOutput) 262 } 263 } 264 265 // DiffsGenerated are only set to true after the block has been fully 266 // validated and integrated. This is required to prevent later blocks from 267 // being accepted on top of an invalid block - if the consensus set ever 268 // forks over an invalid block, 'DiffsGenerated' will be set to 'false', 269 // requiring validation to occur again. when 'DiffsGenerated' is set to 270 // true, validation is skipped, therefore the flag should only be set to 271 // true on fully validated blocks. 272 pb.DiffsGenerated = true 273 274 // Add the block to the current path and block map. 275 bid := pb.Block.ID() 276 blockMap := tx.Bucket(BlockMap) 277 updateCurrentPath(tx, pb, modules.DiffApply) 278 279 // Sanity check preparation - set the consensus hash at this height so that 280 // during reverting a check can be performed to assure consistency when 281 // adding and removing blocks. Must happen after the block is added to the 282 // path. 283 if build.DEBUG { 284 pb.ConsensusChecksum = consensusChecksum(tx) 285 } 286 287 return blockMap.Put(bid[:], encoding.Marshal(*pb)) 288 }