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  }