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  }