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
}
expiration returns the height at which the storage obligation expires.
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)
}