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 }