func (cs *ConsensusSet) LockedTryTransactionSet(fn func(func(txns []types.Transaction) (modules.ConsensusChange, error)) error) error {
	err := cs.tg.Add()
	if err != nil {
		return err
	}
	defer cs.tg.Done()
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	return fn(cs.tryTransactionSet)
}
LockedTryTransactionSet calls fn while under read-lock, passing it a
version of TryTransactionSet that can be called under read-lock. This fixes
an edge case in the transaction pool.
package consensus
import (
	"errors"
	"math/big"
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
	"github.com/coreos/bbolt"
)
var (
	errAlteredRevisionPayouts     = errors.New("file contract revision has altered payout volume")
	errInvalidStorageProof        = errors.New("provided storage proof is invalid")
	errLateRevision               = errors.New("file contract revision submitted after deadline")
	errLowRevisionNumber          = errors.New("transaction has a file contract with an outdated revision number")
	errMissingSiacoinOutput       = errors.New("transaction spends a nonexisting siacoin output")
	errMissingSiafundOutput       = errors.New("transaction spends a nonexisting siafund output")
	errSiacoinInputOutputMismatch = errors.New("siacoin inputs do not equal siacoin outputs for transaction")
	errSiafundInputOutputMismatch = errors.New("siafund inputs do not equal siafund outputs for transaction")
	errUnfinishedFileContract     = errors.New("file contract window has not yet openend")
	errUnrecognizedFileContractID = errors.New("cannot fetch storage proof segment for unknown file contract")
	errWrongUnlockConditions      = errors.New("transaction contains incorrect unlock conditions")
)
func validSiacoins(tx *bolt.Tx, t types.Transaction) error {
	scoBucket := tx.Bucket(SiacoinOutputs)
	var inputSum types.Currency
	for _, sci := range t.SiacoinInputs {
		scoBytes := scoBucket.Get(sci.ParentID[:])
		if scoBytes == nil {
			return errMissingSiacoinOutput
		}
		var sco types.SiacoinOutput
		err := encoding.Unmarshal(scoBytes, &sco)
		if build.DEBUG && err != nil {
			panic(err)
		}
		if sci.UnlockConditions.UnlockHash() != sco.UnlockHash {
			return errWrongUnlockConditions
		}
		inputSum = inputSum.Add(sco.Value)
	}
	if !inputSum.Equals(t.SiacoinOutputSum()) {
		return errSiacoinInputOutputMismatch
	}
	return nil
}
func storageProofSegment(tx *bolt.Tx, fcid types.FileContractID) (uint64, error) {
	fcBucket := tx.Bucket(FileContracts)
	fcBytes := fcBucket.Get(fcid[:])
	if fcBytes == nil {
		return 0, errUnrecognizedFileContractID
	}
	var fc types.FileContract
	err := encoding.Unmarshal(fcBytes, &fc)
	if build.DEBUG && err != nil {
		panic(err)
	}
	blockPath := tx.Bucket(BlockPath)
	triggerHeight := fc.WindowStart - 1
	if triggerHeight > blockHeight(tx) {
		return 0, errUnfinishedFileContract
	}
	var triggerID types.BlockID
	copy(triggerID[:], blockPath.Get(encoding.EncUint64(uint64(triggerHeight))))
	seed := crypto.HashAll(triggerID, fcid)
	numSegments := int64(crypto.CalculateLeaves(fc.FileSize))
	seedInt := new(big.Int).SetBytes(seed[:])
	index := seedInt.Mod(seedInt, big.NewInt(numSegments)).Uint64()
	return index, nil
}
func validStorageProofs100e3(tx *bolt.Tx, t types.Transaction) error {
	for _, sp := range t.StorageProofs {
		segmentIndex, err := storageProofSegment(tx, sp.ParentID)
		if err != nil {
			return err
		}
		fc, err := getFileContract(tx, sp.ParentID)
		if err != nil {
			return err
		}
		leaves := crypto.CalculateLeaves(fc.FileSize)
		segmentLen := uint64(crypto.SegmentSize)
		if segmentIndex == leaves-1 {
			segmentLen = fc.FileSize % crypto.SegmentSize
		}
		if (build.Release == "standard" && blockHeight(tx) < 21e3) || (build.Release == "testing" && blockHeight(tx) < 10) {
			segmentLen = uint64(crypto.SegmentSize)
		}
		verified := crypto.VerifySegment(
			sp.Segment[:segmentLen],
			sp.HashSet,
			leaves,
			segmentIndex,
			fc.FileMerkleRoot,
		)
		if !verified {
			return errInvalidStorageProof
		}
	}
	return nil
}
func validStorageProofs(tx *bolt.Tx, t types.Transaction) error {
	if (build.Release == "standard" && blockHeight(tx) < 100e3) || (build.Release == "testing" && blockHeight(tx) < 10) {
		return validStorageProofs100e3(tx, t)
	}
	for _, sp := range t.StorageProofs {
		segmentIndex, err := storageProofSegment(tx, sp.ParentID)
		if err != nil {
			return err
		}
		fc, err := getFileContract(tx, sp.ParentID)
		if err != nil {
			return err
		}
		leaves := crypto.CalculateLeaves(fc.FileSize)
		segmentLen := uint64(crypto.SegmentSize)
		if segmentIndex == leaves-1 {
			segmentLen = fc.FileSize % crypto.SegmentSize
		}
		if segmentLen == 0 {
			segmentLen = uint64(crypto.SegmentSize)
		}
		verified := crypto.VerifySegment(
			sp.Segment[:segmentLen],
			sp.HashSet,
			leaves,
			segmentIndex,
			fc.FileMerkleRoot,
		)
		if !verified && fc.FileSize > 0 {
			return errInvalidStorageProof
		}
	}
	return nil
}
func validFileContractRevisions(tx *bolt.Tx, t types.Transaction) error {
	for _, fcr := range t.FileContractRevisions {
		fc, err := getFileContract(tx, fcr.ParentID)
		if err != nil {
			return err
		}
		if blockHeight(tx) > fc.WindowStart {
			return errLateRevision
		}
		if fc.RevisionNumber >= fcr.NewRevisionNumber {
			return errLowRevisionNumber
		}
		if fcr.UnlockConditions.UnlockHash() != fc.UnlockHash {
			return errWrongUnlockConditions
		}
		var validPayout, missedPayout, oldPayout types.Currency
		for _, output := range fcr.NewValidProofOutputs {
			validPayout = validPayout.Add(output.Value)
		}
		for _, output := range fcr.NewMissedProofOutputs {
			missedPayout = missedPayout.Add(output.Value)
		}
		for _, output := range fc.ValidProofOutputs {
			oldPayout = oldPayout.Add(output.Value)
		}
		if !validPayout.Equals(oldPayout) {
			return errAlteredRevisionPayouts
		}
		if !missedPayout.Equals(oldPayout) {
			return errAlteredRevisionPayouts
		}
	}
	return nil
}
func validSiafunds(tx *bolt.Tx, t types.Transaction) (err error) {
	var siafundInputSum types.Currency
	var siafundOutputSum types.Currency
	for _, sfi := range t.SiafundInputs {
		sfo, err := getSiafundOutput(tx, sfi.ParentID)
		if err != nil {
			return err
		}
		if sfi.UnlockConditions.UnlockHash() != sfo.UnlockHash {
			return errWrongUnlockConditions
		}
		siafundInputSum = siafundInputSum.Add(sfo.Value)
	}
	for _, sfo := range t.SiafundOutputs {
		siafundOutputSum = siafundOutputSum.Add(sfo.Value)
	}
	if !siafundOutputSum.Equals(siafundInputSum) {
		return errSiafundInputOutputMismatch
	}
	return
}
func validTransaction(tx *bolt.Tx, t types.Transaction) error {
	err := t.StandaloneValid(blockHeight(tx))
	if err != nil {
		return err
	}
	err = validSiacoins(tx, t)
	if err != nil {
		return err
	}
	err = validStorageProofs(tx, t)
	if err != nil {
		return err
	}
	err = validFileContractRevisions(tx, t)
	if err != nil {
		return err
	}
	err = validSiafunds(tx, t)
	if err != nil {
		return err
	}
	return nil
}
func (cs *ConsensusSet) tryTransactionSet(txns []types.Transaction) (modules.ConsensusChange, error) {
	diffHolder := new(processedBlock)
	errSuccess := errors.New("success")
	err := cs.db.Update(func(tx *bolt.Tx) error {
		diffHolder.Height = blockHeight(tx)
		for _, txn := range txns {
			err := validTransaction(tx, txn)
			if err != nil {
				return err
			}
			applyTransaction(tx, diffHolder, txn)
		}
		return errSuccess
	})
	if err != errSuccess {
		return modules.ConsensusChange{}, err
	}
	cc := modules.ConsensusChange{
		SiacoinOutputDiffs:        diffHolder.SiacoinOutputDiffs,
		FileContractDiffs:         diffHolder.FileContractDiffs,
		SiafundOutputDiffs:        diffHolder.SiafundOutputDiffs,
		DelayedSiacoinOutputDiffs: diffHolder.DelayedSiacoinOutputDiffs,
		SiafundPoolDiffs:          diffHolder.SiafundPoolDiffs,
	}
	return cc, nil
}
func (cs *ConsensusSet) TryTransactionSet(txns []types.Transaction) (modules.ConsensusChange, error) {
	err := cs.tg.Add()
	if err != nil {
		return modules.ConsensusChange{}, err
	}
	defer cs.tg.Done()
	cs.mu.RLock()
	defer cs.mu.RUnlock()
	return cs.tryTransactionSet(txns)
}