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