func (so storageObligation) payouts() (valid []types.SiacoinOutput, missed []types.SiacoinOutput) {
	valid = make([]types.SiacoinOutput, 2)
	missed = make([]types.SiacoinOutput, 2)
	if len(so.RevisionTransactionSet) > 0 {
		copy(valid, so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1].FileContractRevisions[0].NewValidProofOutputs)
		copy(missed, so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1].FileContractRevisions[0].NewMissedProofOutputs)
		return
	}
	copy(valid, so.OriginTransactionSet[len(so.OriginTransactionSet)-1].FileContracts[0].ValidProofOutputs)
	copy(missed, so.OriginTransactionSet[len(so.OriginTransactionSet)-1].FileContracts[0].MissedProofOutputs)
	return
}
payous returns the set of valid payouts and missed payouts that represent
the latest revision for the storage obligation.
package host
import (
	"encoding/binary"
	"encoding/json"
	"errors"
	"strconv"
	"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"
)
const (
	obligationUnresolved storageObligationStatus = iota  
	obligationRejected                                   
	obligationSucceeded                                  
	obligationFailed                                     
)
var (
	errDuplicateStorageObligation = errors.New("storage obligation has a file contract which conflicts with an existing storage obligation")
	errInsaneFileContractOutputCounts = errors.New("file contract has incorrect number of outputs for the valid or missed payouts")
	errInsaneFileContractRevisionOutputCounts = errors.New("file contract revision has incorrect number of outputs for the valid or missed payouts")
	errInsaneOriginSetFileContract = errors.New("origin transaction set of storage obligation should have one file contract in the final transaction")
	errInsaneOriginSetSize = errors.New("origin transaction set of storage obligation is size zero")
	errInsaneRevisionSetRevisionCount = errors.New("revision transaction set of storage obligation should have one file contract revision in the final transaction")
	errInsaneStorageObligationRevision = errors.New("revision to storage obligation does not make sense")
	errInsaneStorageObligationRevisionData = errors.New("revision to storage obligation has insane data")
	errNoBuffer = errors.New("file contract rejected because storage proof window is too close")
	errNoStorageObligation = errors.New("storage obligation not found in database")
	errObligationUnlocked = errors.New("storage obligation is unlocked, and should not be getting unlocked")
)
type storageObligationStatus uint64
type storageObligation struct {
	SectorRoots []crypto.Hash
	ContractCost             types.Currency
	LockedCollateral         types.Currency
	PotentialDownloadRevenue types.Currency
	PotentialStorageRevenue  types.Currency
	PotentialUploadRevenue   types.Currency
	RiskedCollateral         types.Currency
	TransactionFeesAdded     types.Currency
	NegotiationHeight      types.BlockHeight
	OriginTransactionSet   []types.Transaction
	RevisionTransactionSet []types.Transaction
	ObligationStatus    storageObligationStatus
	OriginConfirmed     bool
	ProofConfirmed      bool
	ProofConstructed    bool
	RevisionConfirmed   bool
	RevisionConstructed bool
}
func (i storageObligationStatus) String() string {
	if i == 0 {
		return "obligationUnresolved"
	}
	if i == 1 {
		return "obligationRejected"
	}
	if i == 2 {
		return "obligationSucceeded"
	}
	if i == 3 {
		return "obligationFailed"
	}
	return "storageObligationStatus(" + strconv.FormatInt(int64(i), 10) + ")"
}
func getStorageObligation(tx *bolt.Tx, soid types.FileContractID) (so storageObligation, err error) {
	soBytes := tx.Bucket(bucketStorageObligations).Get(soid[:])
	if soBytes == nil {
		return storageObligation{}, errNoStorageObligation
	}
	err = json.Unmarshal(soBytes, &so)
	if err != nil {
		return storageObligation{}, err
	}
	return so, nil
}
func putStorageObligation(tx *bolt.Tx, so storageObligation) error {
	soBytes, err := json.Marshal(so)
	if err != nil {
		return err
	}
	soid := so.id()
	return tx.Bucket(bucketStorageObligations).Put(soid[:], soBytes)
}
func (so storageObligation) expiration() types.BlockHeight {
	if len(so.RevisionTransactionSet) > 0 {
		return so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1].FileContractRevisions[0].NewWindowStart
	}
	return so.OriginTransactionSet[len(so.OriginTransactionSet)-1].FileContracts[0].WindowStart
}
func (so storageObligation) fileSize() uint64 {
	if len(so.RevisionTransactionSet) > 0 {
		return so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1].FileContractRevisions[0].NewFileSize
	}
	return so.OriginTransactionSet[len(so.OriginTransactionSet)-1].FileContracts[0].FileSize
}
func (so storageObligation) id() types.FileContractID {
	return so.OriginTransactionSet[len(so.OriginTransactionSet)-1].FileContractID(0)
}
func (so storageObligation) isSane() error {
	if len(so.OriginTransactionSet) == 0 {
		build.Critical("origin transaction set is empty")
		return errInsaneOriginSetSize
	}
	final := len(so.OriginTransactionSet) - 1
	fcCount := len(so.OriginTransactionSet[final].FileContracts)
	if fcCount != 1 {
		build.Critical("wrong number of file contracts associated with storage obligation:", fcCount)
		return errInsaneOriginSetFileContract
	}
	lenVPOs := len(so.OriginTransactionSet[final].FileContracts[0].ValidProofOutputs)
	lenMPOs := len(so.OriginTransactionSet[final].FileContracts[0].MissedProofOutputs)
	if lenVPOs != 2 || lenMPOs != 2 {
		build.Critical("file contract has wrong number of VPOs and MPOs, expecting 2 each:", lenVPOs, lenMPOs)
		return errInsaneFileContractOutputCounts
	}
	if len(so.RevisionTransactionSet) > 0 {
		final = len(so.OriginTransactionSet) - 1
		fcrCount := len(so.OriginTransactionSet[final].FileContractRevisions)
		if fcrCount != 1 {
			build.Critical("wrong number of file contract revisions in final transaction of revision transaction set:", fcrCount)
			return errInsaneRevisionSetRevisionCount
		}
		lenVPOs = len(so.RevisionTransactionSet[final].FileContractRevisions[0].NewValidProofOutputs)
		lenMPOs = len(so.RevisionTransactionSet[final].FileContractRevisions[0].NewMissedProofOutputs)
		if lenVPOs != 2 || lenMPOs != 2 {
			build.Critical("file contract has wrong number of VPOs and MPOs, expecting 2 each:", lenVPOs, lenMPOs)
			return errInsaneFileContractRevisionOutputCounts
		}
	}
	return nil
}
func (so storageObligation) merkleRoot() crypto.Hash {
	if len(so.RevisionTransactionSet) > 0 {
		return so.RevisionTransactionSet[len(so.RevisionTransactionSet)-1].FileContractRevisions[0].NewFileMerkleRoot
	}
	return so.OriginTransactionSet[len(so.OriginTransactionSet)-1].FileContracts[0].FileMerkleRoot
}