gitlab.com/jokerrs1/Sia@v1.3.2/node/api/host.go (about) 1 package api 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 8 "github.com/NebulousLabs/Sia/build" 9 "github.com/NebulousLabs/Sia/modules" 10 "github.com/NebulousLabs/Sia/types" 11 12 "github.com/julienschmidt/httprouter" 13 ) 14 15 var ( 16 // errNoPath is returned when a call fails to provide a nonempty string 17 // for the path parameter. 18 errNoPath = Error{"path parameter is required"} 19 20 // errStorageFolderNotFound is returned if a call is made looking for a 21 // storage folder which does not appear to exist within the storage 22 // manager. 23 errStorageFolderNotFound = errors.New("storage folder with the provided path could not be found") 24 ) 25 26 type ( 27 // HostGET contains the information that is returned after a GET request to 28 // /host - a bunch of information about the status of the host. 29 HostGET struct { 30 ExternalSettings modules.HostExternalSettings `json:"externalsettings"` 31 FinancialMetrics modules.HostFinancialMetrics `json:"financialmetrics"` 32 InternalSettings modules.HostInternalSettings `json:"internalsettings"` 33 NetworkMetrics modules.HostNetworkMetrics `json:"networkmetrics"` 34 ConnectabilityStatus modules.HostConnectabilityStatus `json:"connectabilitystatus"` 35 WorkingStatus modules.HostWorkingStatus `json:"workingstatus"` 36 } 37 38 // HostEstimateScoreGET contains the information that is returned from a 39 // /host/estimatescore call. 40 HostEstimateScoreGET struct { 41 EstimatedScore types.Currency `json:"estimatedscore"` 42 ConversionRate float64 `json:"conversionrate"` 43 } 44 45 // StorageGET contains the information that is returned after a GET request 46 // to /host/storage - a bunch of information about the status of storage 47 // management on the host. 48 StorageGET struct { 49 Folders []modules.StorageFolderMetadata `json:"folders"` 50 } 51 ) 52 53 // folderIndex determines the index of the storage folder with the provided 54 // path. 55 func folderIndex(folderPath string, storageFolders []modules.StorageFolderMetadata) (int, error) { 56 for _, sf := range storageFolders { 57 if sf.Path == folderPath { 58 return int(sf.Index), nil 59 } 60 } 61 return -1, errStorageFolderNotFound 62 } 63 64 // hostHandlerGET handles GET requests to the /host API endpoint, returning key 65 // information about the host. 66 func (api *API) hostHandlerGET(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 67 es := api.host.ExternalSettings() 68 fm := api.host.FinancialMetrics() 69 is := api.host.InternalSettings() 70 nm := api.host.NetworkMetrics() 71 cs := api.host.ConnectabilityStatus() 72 ws := api.host.WorkingStatus() 73 hg := HostGET{ 74 ExternalSettings: es, 75 FinancialMetrics: fm, 76 InternalSettings: is, 77 NetworkMetrics: nm, 78 ConnectabilityStatus: cs, 79 WorkingStatus: ws, 80 } 81 WriteJSON(w, hg) 82 } 83 84 // parseHostSettings a request's query strings and returns a 85 // modules.HostInternalSettings configured with the request's query string 86 // parameters. 87 func (api *API) parseHostSettings(req *http.Request) (modules.HostInternalSettings, error) { 88 settings := api.host.InternalSettings() 89 90 if req.FormValue("acceptingcontracts") != "" { 91 var x bool 92 _, err := fmt.Sscan(req.FormValue("acceptingcontracts"), &x) 93 if err != nil { 94 return modules.HostInternalSettings{}, err 95 } 96 settings.AcceptingContracts = x 97 } 98 if req.FormValue("maxdownloadbatchsize") != "" { 99 var x uint64 100 _, err := fmt.Sscan(req.FormValue("maxdownloadbatchsize"), &x) 101 if err != nil { 102 return modules.HostInternalSettings{}, err 103 } 104 settings.MaxDownloadBatchSize = x 105 } 106 if req.FormValue("maxduration") != "" { 107 var x types.BlockHeight 108 _, err := fmt.Sscan(req.FormValue("maxduration"), &x) 109 if err != nil { 110 return modules.HostInternalSettings{}, err 111 } 112 settings.MaxDuration = x 113 } 114 if req.FormValue("maxrevisebatchsize") != "" { 115 var x uint64 116 _, err := fmt.Sscan(req.FormValue("maxrevisebatchsize"), &x) 117 if err != nil { 118 return modules.HostInternalSettings{}, err 119 } 120 settings.MaxReviseBatchSize = x 121 } 122 if req.FormValue("netaddress") != "" { 123 var x modules.NetAddress 124 _, err := fmt.Sscan(req.FormValue("netaddress"), &x) 125 if err != nil { 126 return modules.HostInternalSettings{}, err 127 } 128 settings.NetAddress = x 129 } 130 if req.FormValue("windowsize") != "" { 131 var x types.BlockHeight 132 _, err := fmt.Sscan(req.FormValue("windowsize"), &x) 133 if err != nil { 134 return modules.HostInternalSettings{}, err 135 } 136 settings.WindowSize = x 137 } 138 139 if req.FormValue("collateral") != "" { 140 var x types.Currency 141 _, err := fmt.Sscan(req.FormValue("collateral"), &x) 142 if err != nil { 143 return modules.HostInternalSettings{}, err 144 } 145 settings.Collateral = x 146 } 147 if req.FormValue("collateralbudget") != "" { 148 var x types.Currency 149 _, err := fmt.Sscan(req.FormValue("collateralbudget"), &x) 150 if err != nil { 151 return modules.HostInternalSettings{}, err 152 } 153 settings.CollateralBudget = x 154 } 155 if req.FormValue("maxcollateral") != "" { 156 var x types.Currency 157 _, err := fmt.Sscan(req.FormValue("maxcollateral"), &x) 158 if err != nil { 159 return modules.HostInternalSettings{}, err 160 } 161 settings.MaxCollateral = x 162 } 163 164 if req.FormValue("mincontractprice") != "" { 165 var x types.Currency 166 _, err := fmt.Sscan(req.FormValue("mincontractprice"), &x) 167 if err != nil { 168 return modules.HostInternalSettings{}, err 169 } 170 settings.MinContractPrice = x 171 } 172 if req.FormValue("mindownloadbandwidthprice") != "" { 173 var x types.Currency 174 _, err := fmt.Sscan(req.FormValue("mindownloadbandwidthprice"), &x) 175 if err != nil { 176 return modules.HostInternalSettings{}, err 177 } 178 settings.MinDownloadBandwidthPrice = x 179 } 180 if req.FormValue("minstorageprice") != "" { 181 var x types.Currency 182 _, err := fmt.Sscan(req.FormValue("minstorageprice"), &x) 183 if err != nil { 184 return modules.HostInternalSettings{}, err 185 } 186 settings.MinStoragePrice = x 187 } 188 if req.FormValue("minuploadbandwidthprice") != "" { 189 var x types.Currency 190 _, err := fmt.Sscan(req.FormValue("minuploadbandwidthprice"), &x) 191 if err != nil { 192 return modules.HostInternalSettings{}, err 193 } 194 settings.MinUploadBandwidthPrice = x 195 } 196 197 return settings, nil 198 } 199 200 // hostEstimateScoreGET handles the POST request to /host/estimatescore and 201 // computes an estimated HostDB score for the provided settings. 202 func (api *API) hostEstimateScoreGET(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 203 // This call requires a renter, check that it is present. 204 if api.renter == nil { 205 WriteError(w, Error{"cannot call /host/estimatescore without the renter module"}, http.StatusBadRequest) 206 return 207 } 208 209 settings, err := api.parseHostSettings(req) 210 if err != nil { 211 WriteError(w, Error{"error parsing host settings: " + err.Error()}, http.StatusBadRequest) 212 return 213 } 214 var totalStorage, remainingStorage uint64 215 for _, sf := range api.host.StorageFolders() { 216 totalStorage += sf.Capacity 217 remainingStorage += sf.CapacityRemaining 218 } 219 mergedSettings := modules.HostExternalSettings{ 220 AcceptingContracts: settings.AcceptingContracts, 221 MaxDownloadBatchSize: settings.MaxDownloadBatchSize, 222 MaxDuration: settings.MaxDuration, 223 MaxReviseBatchSize: settings.MaxReviseBatchSize, 224 RemainingStorage: remainingStorage, 225 SectorSize: modules.SectorSize, 226 TotalStorage: totalStorage, 227 WindowSize: settings.WindowSize, 228 229 Collateral: settings.Collateral, 230 MaxCollateral: settings.MaxCollateral, 231 232 ContractPrice: settings.MinContractPrice, 233 DownloadBandwidthPrice: settings.MinDownloadBandwidthPrice, 234 StoragePrice: settings.MinStoragePrice, 235 UploadBandwidthPrice: settings.MinUploadBandwidthPrice, 236 237 Version: build.Version, 238 } 239 entry := modules.HostDBEntry{} 240 entry.PublicKey = api.host.PublicKey() 241 entry.HostExternalSettings = mergedSettings 242 estimatedScoreBreakdown := api.renter.EstimateHostScore(entry) 243 e := HostEstimateScoreGET{ 244 EstimatedScore: estimatedScoreBreakdown.Score, 245 ConversionRate: estimatedScoreBreakdown.ConversionRate, 246 } 247 WriteJSON(w, e) 248 } 249 250 // hostHandlerPOST handles POST request to the /host API endpoint, which sets 251 // the internal settings of the host. 252 func (api *API) hostHandlerPOST(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 253 settings, err := api.parseHostSettings(req) 254 if err != nil { 255 WriteError(w, Error{"error parsing host settings: " + err.Error()}, http.StatusBadRequest) 256 return 257 } 258 259 err = api.host.SetInternalSettings(settings) 260 if err != nil { 261 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 262 return 263 } 264 WriteSuccess(w) 265 } 266 267 // hostAnnounceHandler handles the API call to get the host to announce itself 268 // to the network. 269 func (api *API) hostAnnounceHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 270 var err error 271 if addr := req.FormValue("netaddress"); addr != "" { 272 err = api.host.AnnounceAddress(modules.NetAddress(addr)) 273 } else { 274 err = api.host.Announce() 275 } 276 if err != nil { 277 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 278 return 279 } 280 WriteSuccess(w) 281 } 282 283 // storageHandler returns a bunch of information about storage management on 284 // the host. 285 func (api *API) storageHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 286 WriteJSON(w, StorageGET{ 287 Folders: api.host.StorageFolders(), 288 }) 289 } 290 291 // storageFoldersAddHandler adds a storage folder to the storage manager. 292 func (api *API) storageFoldersAddHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 293 folderPath := req.FormValue("path") 294 var folderSize uint64 295 _, err := fmt.Sscan(req.FormValue("size"), &folderSize) 296 if err != nil { 297 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 298 return 299 } 300 err = api.host.AddStorageFolder(folderPath, folderSize) 301 if err != nil { 302 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 303 return 304 } 305 WriteSuccess(w) 306 } 307 308 // storageFoldersResizeHandler resizes a storage folder in the storage manager. 309 func (api *API) storageFoldersResizeHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 310 folderPath := req.FormValue("path") 311 if folderPath == "" { 312 WriteError(w, Error{"path parameter is required"}, http.StatusBadRequest) 313 return 314 } 315 316 storageFolders := api.host.StorageFolders() 317 folderIndex, err := folderIndex(folderPath, storageFolders) 318 if err != nil { 319 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 320 return 321 } 322 323 var newSize uint64 324 _, err = fmt.Sscan(req.FormValue("newsize"), &newSize) 325 if err != nil { 326 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 327 return 328 } 329 err = api.host.ResizeStorageFolder(uint16(folderIndex), newSize, false) 330 if err != nil { 331 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 332 return 333 } 334 WriteSuccess(w) 335 } 336 337 // storageFoldersRemoveHandler removes a storage folder from the storage 338 // manager. 339 func (api *API) storageFoldersRemoveHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 340 folderPath := req.FormValue("path") 341 if folderPath == "" { 342 WriteError(w, Error{"path parameter is required"}, http.StatusBadRequest) 343 return 344 } 345 346 storageFolders := api.host.StorageFolders() 347 folderIndex, err := folderIndex(folderPath, storageFolders) 348 if err != nil { 349 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 350 return 351 } 352 353 force := req.FormValue("force") == "true" 354 err = api.host.RemoveStorageFolder(uint16(folderIndex), force) 355 if err != nil { 356 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 357 return 358 } 359 WriteSuccess(w) 360 } 361 362 // storageSectorsDeleteHandler handles the call to delete a sector from the 363 // storage manager. 364 func (api *API) storageSectorsDeleteHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { 365 sectorRoot, err := scanHash(ps.ByName("merkleroot")) 366 if err != nil { 367 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 368 return 369 } 370 err = api.host.DeleteSector(sectorRoot) 371 if err != nil { 372 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 373 return 374 } 375 WriteSuccess(w) 376 }