github.com/nebulouslabs/sia@v1.3.7/modules/host/persist.go (about)

     1  package host
     2  
     3  import (
     4  	"encoding/json"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/NebulousLabs/Sia/build"
     9  	"github.com/NebulousLabs/Sia/crypto"
    10  	"github.com/NebulousLabs/Sia/modules"
    11  	"github.com/NebulousLabs/Sia/persist"
    12  	"github.com/NebulousLabs/Sia/types"
    13  
    14  	"github.com/coreos/bbolt"
    15  )
    16  
    17  // persistence is the data that is kept when the host is restarted.
    18  type persistence struct {
    19  	// Consensus Tracking.
    20  	BlockHeight  types.BlockHeight         `json:"blockheight"`
    21  	RecentChange modules.ConsensusChangeID `json:"recentchange"`
    22  
    23  	// Host Identity.
    24  	Announced        bool                         `json:"announced"`
    25  	AutoAddress      modules.NetAddress           `json:"autoaddress"`
    26  	FinancialMetrics modules.HostFinancialMetrics `json:"financialmetrics"`
    27  	PublicKey        types.SiaPublicKey           `json:"publickey"`
    28  	RevisionNumber   uint64                       `json:"revisionnumber"`
    29  	SecretKey        crypto.SecretKey             `json:"secretkey"`
    30  	Settings         modules.HostInternalSettings `json:"settings"`
    31  	UnlockHash       types.UnlockHash             `json:"unlockhash"`
    32  }
    33  
    34  // persistData returns the data in the Host that will be saved to disk.
    35  func (h *Host) persistData() persistence {
    36  	return persistence{
    37  		// Consensus Tracking.
    38  		BlockHeight:  h.blockHeight,
    39  		RecentChange: h.recentChange,
    40  
    41  		// Host Identity.
    42  		Announced:        h.announced,
    43  		AutoAddress:      h.autoAddress,
    44  		FinancialMetrics: h.financialMetrics,
    45  		PublicKey:        h.publicKey,
    46  		RevisionNumber:   h.revisionNumber,
    47  		SecretKey:        h.secretKey,
    48  		Settings:         h.settings,
    49  		UnlockHash:       h.unlockHash,
    50  	}
    51  }
    52  
    53  // establishDefaults configures the default settings for the host, overwriting
    54  // any existing settings.
    55  func (h *Host) establishDefaults() error {
    56  	// Configure the settings object.
    57  	h.settings = modules.HostInternalSettings{
    58  		MaxDownloadBatchSize: uint64(defaultMaxDownloadBatchSize),
    59  		MaxDuration:          defaultMaxDuration,
    60  		MaxReviseBatchSize:   uint64(defaultMaxReviseBatchSize),
    61  		WindowSize:           defaultWindowSize,
    62  
    63  		Collateral:       defaultCollateral,
    64  		CollateralBudget: defaultCollateralBudget,
    65  		MaxCollateral:    defaultMaxCollateral,
    66  
    67  		MinStoragePrice:           defaultStoragePrice,
    68  		MinContractPrice:          defaultContractPrice,
    69  		MinDownloadBandwidthPrice: defaultDownloadBandwidthPrice,
    70  		MinUploadBandwidthPrice:   defaultUploadBandwidthPrice,
    71  	}
    72  
    73  	// Generate signing key, for revising contracts.
    74  	sk, pk := crypto.GenerateKeyPair()
    75  	h.secretKey = sk
    76  	h.publicKey = types.Ed25519PublicKey(pk)
    77  
    78  	// Subscribe to the consensus set.
    79  	err := h.initConsensusSubscription()
    80  	if err != nil {
    81  		return err
    82  	}
    83  	return nil
    84  }
    85  
    86  // loadPersistObject will take a persist object and copy the data into the
    87  // host.
    88  func (h *Host) loadPersistObject(p *persistence) {
    89  	// Copy over consensus tracking.
    90  	h.blockHeight = p.BlockHeight
    91  	h.recentChange = p.RecentChange
    92  
    93  	// Copy over host identity.
    94  	h.announced = p.Announced
    95  	h.autoAddress = p.AutoAddress
    96  	if err := p.AutoAddress.IsValid(); err != nil {
    97  		h.log.Printf("WARN: AutoAddress '%v' loaded from persist is invalid: %v", p.AutoAddress, err)
    98  		h.autoAddress = ""
    99  	}
   100  	h.financialMetrics = p.FinancialMetrics
   101  	h.publicKey = p.PublicKey
   102  	h.revisionNumber = p.RevisionNumber
   103  	h.secretKey = p.SecretKey
   104  	h.settings = p.Settings
   105  	if err := p.Settings.NetAddress.IsValid(); err != nil {
   106  		h.log.Printf("WARN: NetAddress '%v' loaded from persist is invalid: %v", p.Settings.NetAddress, err)
   107  		h.settings.NetAddress = ""
   108  	}
   109  	h.unlockHash = p.UnlockHash
   110  }
   111  
   112  // initDB will check that the database has been initialized and if not, will
   113  // initialize the database.
   114  func (h *Host) initDB() (err error) {
   115  	// Open the host's database and set up the stop function to close it.
   116  	h.db, err = h.dependencies.OpenDatabase(dbMetadata, filepath.Join(h.persistDir, dbFilename))
   117  	if err != nil {
   118  		return err
   119  	}
   120  	h.tg.AfterStop(func() {
   121  		err = h.db.Close()
   122  		if err != nil {
   123  			h.log.Println("Could not close the database:", err)
   124  		}
   125  	})
   126  
   127  	return h.db.Update(func(tx *bolt.Tx) error {
   128  		// The storage obligation bucket does not exist, which means the
   129  		// database needs to be initialized. Create the database buckets.
   130  		buckets := [][]byte{
   131  			bucketActionItems,
   132  			bucketStorageObligations,
   133  		}
   134  		for _, bucket := range buckets {
   135  			_, err := tx.CreateBucketIfNotExists(bucket)
   136  			if err != nil {
   137  				return err
   138  			}
   139  		}
   140  		return nil
   141  	})
   142  }
   143  
   144  // load loads the Hosts's persistent data from disk.
   145  func (h *Host) load() error {
   146  	// Initialize the host database.
   147  	err := h.initDB()
   148  	if err != nil {
   149  		err = build.ExtendErr("Could not initialize database:", err)
   150  		h.log.Println(err)
   151  		return err
   152  	}
   153  
   154  	// Load the old persistence object from disk. Simple task if the version is
   155  	// the most recent version, but older versions need to be updated to the
   156  	// more recent structures.
   157  	p := new(persistence)
   158  	err = h.dependencies.LoadFile(persistMetadata, p, filepath.Join(h.persistDir, settingsFile))
   159  	if err == nil {
   160  		// Copy in the persistence.
   161  		h.loadPersistObject(p)
   162  	} else if os.IsNotExist(err) {
   163  		// There is no host.json file, set up sane defaults.
   164  		return h.establishDefaults()
   165  	} else if err == persist.ErrBadVersion {
   166  		// Attempt an upgrade from V112 to V120.
   167  		err = h.upgradeFromV112ToV120()
   168  		if err != nil {
   169  			return err
   170  		}
   171  	} else if err != nil {
   172  		return err
   173  	}
   174  
   175  	// Get the contract count and locked collateral by observing all of the incomplete
   176  	// storage obligations in the database.
   177  	// TODO: both contract count and locked collateral are not correctly updated during
   178  	// contract renewals. This leads to an offset to the real value over time.
   179  	h.financialMetrics.ContractCount = 0
   180  	h.financialMetrics.LockedStorageCollateral = types.NewCurrency64(0)
   181  	err = h.db.View(func(tx *bolt.Tx) error {
   182  		cursor := tx.Bucket(bucketStorageObligations).Cursor()
   183  		for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
   184  			var so storageObligation
   185  			err := json.Unmarshal(v, &so)
   186  			if err != nil {
   187  				return err
   188  			}
   189  			if so.ObligationStatus == obligationUnresolved {
   190  				h.financialMetrics.ContractCount++
   191  				h.financialMetrics.LockedStorageCollateral = h.financialMetrics.LockedStorageCollateral.Add(so.LockedCollateral)
   192  			}
   193  		}
   194  		return nil
   195  	})
   196  	if err != nil {
   197  		return err
   198  	}
   199  
   200  	return h.initConsensusSubscription()
   201  }
   202  
   203  // saveSync stores all of the persist data to disk and then syncs to disk.
   204  func (h *Host) saveSync() error {
   205  	return persist.SaveJSON(persistMetadata, h.persistData(), filepath.Join(h.persistDir, settingsFile))
   206  }