Unverified Commit 9f8d5353 authored by Luke Champine's avatar Luke Champine Committed by GitHub

Merge pull request #3131 from NebulousLabs/3087-fixes

Updates on Spend Reporting PR 3087
parents 6ef4da21 cc81c96a
......@@ -35,8 +35,7 @@ func renterexportcontracttxnscmd(destination string) {
die("Could not retrieve contracts:", err)
}
var contractTxns []types.Transaction
contracts := append(cs.Contracts, cs.OldContracts...)
for _, c := range contracts {
for _, c := range cs.Contracts {
contractTxns = append(contractTxns, c.LastTransaction)
}
destination = abs(destination)
......
......@@ -403,38 +403,78 @@ func (s byValue) Less(i, j int) bool {
// rentercontractscmd is the handler for the comand `siac renter contracts`.
// It lists the Renter's contracts.
func rentercontractscmd() {
rc, err := httpClient.RenterContractsGet()
rc, err := httpClient.RenterInactiveContractsGet()
if err != nil {
die("Could not get contracts:", err)
}
if len(rc.Contracts) == 0 && len(rc.OldContracts) == 0 {
fmt.Println("No contracts have been formed.")
if len(rc.ActiveContracts) == 0 && len(rc.InactiveContracts) == 0 && !renterAllContracts {
fmt.Println("No contracts in the current period.")
return
}
if len(rc.Contracts) == 0 && !renterAllContracts {
fmt.Println("No active contracts.")
return
}
if len(rc.Contracts) != 0 {
sort.Sort(byValue(rc.Contracts))
var totalStored uint64
var totalRemaining, totalSpent, totalFees types.Currency
for _, c := range rc.Contracts {
totalStored += c.Size
totalRemaining = totalRemaining.Add(c.RenterFunds)
totalSpent = totalSpent.Add(c.TotalCost.Sub(c.RenterFunds).Sub(c.Fees))
totalFees = totalFees.Add(c.Fees)
if len(rc.ActiveContracts) != 0 && len(rc.InactiveContracts) != 0 {
// Display Active Contracts
fmt.Println("Contracts in the Current Period")
sort.Sort(byValue(rc.ActiveContracts))
var activeTotalStored uint64
var activeTotalRemaining, activeTotalSpent, activeTotalFees types.Currency
for _, c := range rc.ActiveContracts {
activeTotalStored += c.Size
activeTotalRemaining = activeTotalRemaining.Add(c.RenterFunds)
activeTotalSpent = activeTotalSpent.Add(c.TotalCost.Sub(c.RenterFunds).Sub(c.Fees))
activeTotalFees = activeTotalFees.Add(c.Fees)
}
fmt.Printf(`Active Contract Summary:
"Number of Contracts: %v
"Total stored: %9s
"Total Remaining: %v
"Total Spent: %v
"Total Fees: %v
`, len(rc.Contracts), filesizeUnits(int64(totalStored)), currencyUnits(totalRemaining), currencyUnits(totalSpent), currencyUnits(totalFees))
fmt.Printf(`
Active Contract Summary:
Number of Contracts: %v
Total stored: %9s
Total Remaining: %v
Total Spent: %v
Total Fees: %v
`, len(rc.ActiveContracts), filesizeUnits(int64(activeTotalStored)), currencyUnits(activeTotalRemaining), currencyUnits(activeTotalSpent), currencyUnits(activeTotalFees))
w := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
fmt.Fprintln(w, "Host\tRemaining Funds\tSpent Funds\tSpent Fees\tData\tEnd Height\tID\tGoodForUpload\tGoodForRenew")
for _, c := range rc.Contracts {
for _, c := range rc.ActiveContracts {
address := c.NetAddress
if address == "" {
address = "Host Removed"
}
fmt.Fprintf(w, "%v\t%8s\t%8s\t%8s\t%v\t%v\t%v\t%v\t%v\n",
address,
currencyUnits(c.RenterFunds),
currencyUnits(c.TotalCost.Sub(c.RenterFunds).Sub(c.Fees)),
currencyUnits(c.Fees),
filesizeUnits(int64(c.Size)),
c.EndHeight,
c.ID,
c.GoodForUpload,
c.GoodForRenew)
}
w.Flush()
// Display Inactive Contracts
sort.Sort(byValue(rc.InactiveContracts))
var inactiveTotalStored uint64
var inactiveTotalRemaining, inactiveTotalSpent, inactiveTotalFees types.Currency
for _, c := range rc.InactiveContracts {
inactiveTotalStored += c.Size
inactiveTotalRemaining = inactiveTotalRemaining.Add(c.RenterFunds)
inactiveTotalSpent = inactiveTotalSpent.Add(c.TotalCost.Sub(c.RenterFunds).Sub(c.Fees))
inactiveTotalFees = inactiveTotalFees.Add(c.Fees)
}
fmt.Printf(`
Inactive Contract Summary:
Number of Contracts: %v
Total stored: %9s
Total Remaining: %v
Total Spent: %v
Total Fees: %v
`, len(rc.InactiveContracts), filesizeUnits(int64(inactiveTotalStored)), currencyUnits(inactiveTotalRemaining), currencyUnits(inactiveTotalSpent), currencyUnits(inactiveTotalFees))
w = tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
fmt.Fprintln(w, "Host\tRemaining Funds\tSpent Funds\tSpent Fees\tData\tEnd Height\tID\tGoodForUpload\tGoodForRenew")
for _, c := range rc.InactiveContracts {
address := c.NetAddress
if address == "" {
address = "Host Removed"
......@@ -452,31 +492,41 @@ func rentercontractscmd() {
}
w.Flush()
}
if renterAllContracts {
if len(rc.OldContracts) == 0 {
rce, err := httpClient.RenterExpiredContractsGet()
if err != nil {
die("Could not get expired contracts:", err)
}
if len(rc.ActiveContracts) == 0 && len(rc.InactiveContracts) == 0 && len(rce.ExpiredContracts) == 0 {
fmt.Println("No contracts have been formed.")
return
}
if len(rce.ExpiredContracts) == 0 {
fmt.Println("No expired contracts")
return
}
sort.Sort(byValue(rc.OldContracts))
var totalStored uint64
var totalWithheld, totalSpent, totalFees types.Currency
for _, c := range rc.OldContracts {
totalStored += c.Size
totalWithheld = totalWithheld.Add(c.RenterFunds)
totalSpent = totalSpent.Add(c.TotalCost.Sub(c.RenterFunds).Sub(c.Fees))
totalFees = totalFees.Add(c.Fees)
sort.Sort(byValue(rce.ExpiredContracts))
var expiredTotalStored uint64
var expiredTotalWithheld, expiredTotalSpent, expiredTotalFees types.Currency
for _, c := range rce.ExpiredContracts {
expiredTotalStored += c.Size
expiredTotalWithheld = expiredTotalWithheld.Add(c.RenterFunds)
expiredTotalSpent = expiredTotalSpent.Add(c.TotalCost.Sub(c.RenterFunds).Sub(c.Fees))
expiredTotalFees = expiredTotalFees.Add(c.Fees)
}
fmt.Printf(`
Expired Contract Summary:
"Number of Contracts: %v
"Total stored: %9s
"Total Remaining: %v
"Total Spent: %v
"Total Fees: %v
`, len(rc.OldContracts), filesizeUnits(int64(totalStored)), currencyUnits(totalWithheld), currencyUnits(totalSpent), currencyUnits(totalFees))
Expired Contract Summary:
Number of Contracts: %v
Total stored: %9s
Total Remaining: %v
Total Spent: %v
Total Fees: %v
`, len(rce.ExpiredContracts), filesizeUnits(int64(expiredTotalStored)), currencyUnits(expiredTotalWithheld), currencyUnits(expiredTotalSpent), currencyUnits(expiredTotalFees))
w := tabwriter.NewWriter(os.Stdout, 2, 0, 2, ' ', 0)
fmt.Fprintln(w, "Host\tWithheld Funds\tSpent Funds\tSpent Fees\tData\tEnd Height\tID\tGoodForUpload\tGoodForRenew")
for _, c := range rc.OldContracts {
for _, c := range rce.ExpiredContracts {
address := c.NetAddress
if address == "" {
address = "Host Removed"
......@@ -499,11 +549,17 @@ func rentercontractscmd() {
// rentercontractsviewcmd is the handler for the command `siac renter contracts <id>`.
// It lists details of a specific contract.
func rentercontractsviewcmd(cid string) {
rc, err := httpClient.RenterContractsGet()
rc, err := httpClient.RenterInactiveContractsGet()
if err != nil {
die("Could not get contract details: ", err)
}
contracts := append(rc.Contracts, rc.OldContracts...)
rce, err := httpClient.RenterExpiredContractsGet()
if err != nil {
die("Could not get expired contract details: ", err)
}
contracts := append(rc.ActiveContracts, rc.InactiveContracts...)
contracts = append(contracts, rce.ExpiredContracts...)
for _, rc := range contracts {
if rc.ID.String() == cid {
......
......@@ -935,12 +935,24 @@ standard success or error response. See
#### /renter/contracts [GET]
returns active contracts. Expired contracts are not included.
returns the renter's contracts. Active contracts are contracts that the Renter
is currently using to store, upload, and download data, and are returned by
default. Inactive contracts are contracts that are in the current period but are
marked as not good for renew, these contracts have the potential to become
active again but currently are not storing data. Expired contracts are
contracts not in the current period, where not more data is being stored and
excess funds have been released to the renter.
###### Contract Parameters [(with comments)](/doc/api/Renter.md#contract-parameters)
```
inactive // true or false - Optional
expired // true or false - Optional
```
###### JSON Response [(with comments)](/doc/api/Renter.md#json-response-1)
```javascript
{
"contracts": [
"activecontracts": [
{
"downloadspending": "1234", // hastings
"endheight": 50000, // block height
......@@ -962,7 +974,9 @@ returns active contracts. Expired contracts are not included.
"goodforupload": true,
"goodforrenew": false,
}
]
],
"inactivecontracts": [],
"expiredcontracts": [],
}
```
......
......@@ -63,11 +63,11 @@ returns the current settings along with metrics on the renter's spending.
// Is always nonzero.
"renewwindow": 3024 // blocks
},
// MaxUploadSpeed by defaul is unlimited but can be set by the user to
// MaxUploadSpeed by default is unlimited but can be set by the user to
// manage bandwidth
"maxuploadspeed": 1234, // bytes per second
// MaxDownloadSpeed by defaul is unlimited but can be set by the user to
// MaxDownloadSpeed by default is unlimited but can be set by the user to
// manage bandwidth
"maxdownloadspeed": 1234, // bytes per second
......@@ -148,12 +148,24 @@ standard success or error response. See
#### /renter/contracts [GET]
returns active contracts. Expired contracts are not included.
returns the renter's contracts. Active contracts are contracts that the Renter
is currently using to store, upload, and download data, and are returned by
default. Inactive contracts are contracts that are in the current period but are
marked as not good for renew, these contracts have the potential to become
active again but currently are not storing data. Expired contracts are
contracts not in the current period, where not more data is being stored and
excess funds have been released to the renter.
###### Contract Parameters
```
inactive // true or false - Optional
expired // true or false - Optional
```
###### JSON Response
```javascript
{
"contracts": [
"activecontracts": [
{
// Amount of contract funds that have been spent on downloads.
"downloadspending": "1234", // hastings
......@@ -213,7 +225,9 @@ returns active contracts. Expired contracts are not included.
// Signals if contract is good for a renewal
"goodforrenew": false,
}
]
],
"inactivecontracts": [],
"expiredcontracts": [],
}
```
......
......@@ -322,10 +322,10 @@ type Renter interface {
// Close closes the Renter.
Close() error
// Contracts returns the active contracts formed by the renter.
// Contracts returns the staticContracts of the renter's hostContractor.
Contracts() []RenterContract
// OldContracts returns the old contracts formed by the renter.
// OldContracts returns the oldContracts of the renter's hostContractor.
OldContracts() []RenterContract
// ContractUtility provides the contract utility for a given host key.
......
......@@ -96,34 +96,34 @@ func (c *Contractor) PeriodSpending() modules.ContractorSpending {
}
// Calculate needed spending to be reported from old contracts
for _, old := range c.oldContracts {
host, exist := c.hdb.Host(old.HostPublicKey)
if old.StartHeight >= c.currentPeriod {
for _, contract := range c.oldContracts {
host, exist := c.hdb.Host(contract.HostPublicKey)
if contract.StartHeight >= c.currentPeriod {
// Calculate spending from contracts that were renewed during the current period
// Calculate ContractFees
spending.ContractFees = spending.ContractFees.Add(old.ContractFee)
spending.ContractFees = spending.ContractFees.Add(old.TxnFee)
spending.ContractFees = spending.ContractFees.Add(old.SiafundFee)
spending.ContractFees = spending.ContractFees.Add(contract.ContractFee)
spending.ContractFees = spending.ContractFees.Add(contract.TxnFee)
spending.ContractFees = spending.ContractFees.Add(contract.SiafundFee)
// Calculate TotalAllocated
spending.TotalAllocated = spending.TotalAllocated.Add(old.TotalCost)
spending.TotalAllocated = spending.TotalAllocated.Add(contract.TotalCost)
// Calculate Spending
spending.DownloadSpending = spending.DownloadSpending.Add(old.DownloadSpending)
spending.UploadSpending = spending.UploadSpending.Add(old.UploadSpending)
spending.StorageSpending = spending.StorageSpending.Add(old.StorageSpending)
} else if exist && old.EndHeight+host.WindowSize+types.MaturityDelay > c.blockHeight {
spending.DownloadSpending = spending.DownloadSpending.Add(contract.DownloadSpending)
spending.UploadSpending = spending.UploadSpending.Add(contract.UploadSpending)
spending.StorageSpending = spending.StorageSpending.Add(contract.StorageSpending)
} else if exist && contract.EndHeight+host.WindowSize+types.MaturityDelay > c.blockHeight {
// Calculate funds that are being withheld in contracts
spending.WithheldFunds = spending.WithheldFunds.Add(old.RenterFunds)
spending.WithheldFunds = spending.WithheldFunds.Add(contract.RenterFunds)
// Record the largest window size for worst case when reporting the spending
if host.WindowSize >= spending.ReleaseBlock {
spending.ReleaseBlock = host.WindowSize
if contract.EndHeight+host.WindowSize+types.MaturityDelay >= spending.ReleaseBlock {
spending.ReleaseBlock = contract.EndHeight + host.WindowSize + types.MaturityDelay
}
// Calculate Previous spending
spending.PreviousSpending = spending.PreviousSpending.Add(old.ContractFee).Add(old.TxnFee).
Add(old.SiafundFee).Add(old.DownloadSpending).Add(old.UploadSpending).Add(old.StorageSpending)
spending.PreviousSpending = spending.PreviousSpending.Add(contract.ContractFee).Add(contract.TxnFee).
Add(contract.SiafundFee).Add(contract.DownloadSpending).Add(contract.UploadSpending).Add(contract.StorageSpending)
} else {
// Calculate Previous spending
spending.PreviousSpending = spending.PreviousSpending.Add(old.ContractFee).Add(old.TxnFee).
Add(old.SiafundFee).Add(old.DownloadSpending).Add(old.UploadSpending).Add(old.StorageSpending)
spending.PreviousSpending = spending.PreviousSpending.Add(contract.ContractFee).Add(contract.TxnFee).
Add(contract.SiafundFee).Add(contract.DownloadSpending).Add(contract.UploadSpending).Add(contract.StorageSpending)
}
}
......
......@@ -360,10 +360,10 @@ func (c *Contractor) threadedContractMaintenance() {
// in the current period.
endHeight = currentPeriod + allowance.Period
// Determine how many funds have been used already in this billing
// cycle, and how many funds are remaining. We have to calculate these
// numbers separately to avoid underflow, and then re-join them later to
// get the full picture for how many funds are available.
// Determine how many funds have been used already in this billing cycle,
// and how many funds are remaining. We have to calculate these numbers
// separately to avoid underflow, and then re-join them later to get the
// full picture for how many funds are available.
var fundsUsed types.Currency
for _, contract := range c.staticContracts.ViewAll() {
// Calculate the cost of the contract line.
......@@ -372,33 +372,33 @@ func (c *Contractor) threadedContractMaintenance() {
// Check if the contract is expiring. The funds in the contract are
// handled differently based on this information.
if blockHeight+allowance.RenewWindow >= contract.EndHeight {
// The contract is expiring. Some of the funds are locked down
// to renew the contract, and then the remaining funds can be
// allocated to 'availableFunds'.
// The contract is expiring. Some of the funds are locked down to
// renew the contract, and then the remaining funds can be allocated
// to 'availableFunds'.
fundsUsed = fundsUsed.Add(contractLineCost).Sub(contract.RenterFunds)
fundsAvailable = fundsAvailable.Add(contract.RenterFunds)
} else {
// The contract is not expiring. None of the funds in the
// contract are available to renew or form contracts.
// The contract is not expiring. None of the funds in the contract
// are available to renew or form contracts.
fundsUsed = fundsUsed.Add(contractLineCost)
}
}
// Add any unspent funds from the allowance to the available funds. If
// the allowance has been decreased, it's possible that we actually need
// to reduce the number of funds available to compensate.
// Add any unspent funds from the allowance to the available funds. If the
// allowance has been decreased, it's possible that we actually need to
// reduce the number of funds available to compensate.
if fundsAvailable.Add(allowance.Funds).Cmp(fundsUsed) > 0 {
fundsAvailable = fundsAvailable.Add(allowance.Funds).Sub(fundsUsed)
} else {
// Figure out how much we need to remove from fundsAvailable to
// clear the allowance.
// Figure out how much we need to remove from fundsAvailable to clear
// the allowance.
overspend := fundsUsed.Sub(allowance.Funds).Sub(fundsAvailable)
if fundsAvailable.Cmp(overspend) > 0 {
// We still have some funds available.
fundsAvailable = fundsAvailable.Sub(overspend)
} else {
// The overspend exceeds the available funds, set available
// funds to zero.
// The overspend exceeds the available funds, set available funds to
// zero.
fundsAvailable = types.ZeroCurrency
}
}
......@@ -411,22 +411,21 @@ func (c *Contractor) threadedContractMaintenance() {
continue
}
if blockHeight+allowance.RenewWindow >= contract.EndHeight {
// This contract needs to be renewed because it is going to
// expire soon. First step is to calculate how much money should
// be used in the renewal, based on how much of the contract
// funds (including previous contracts this billing cycle due to
// financial resets) were spent throughout this billing cycle.
// This contract needs to be renewed because it is going to expire
// soon. First step is to calculate how much money should be used in
// the renewal, based on how much of the contract funds (including
// previous contracts this billing cycle due to financial resets)
// were spent throughout this billing cycle.
//
// The amount we care about is the total amount that was spent
// on uploading, downloading, and storage throughout the billing
// cycle. This is calculated by starting with the total cost and
// subtracting out all of the fees, and then all of the unused
// money that was allocated (the RenterFunds).
// The amount we care about is the total amount that was spent on
// uploading, downloading, and storage throughout the billing cycle.
// This is calculated by starting with the total cost and
// subtracting out all of the fees, and then all of the unused money
// that was allocated (the RenterFunds).
//
// In order to accurately fund contracts based on variable spending,
// the cost per block is calculated based on the total spent
// over the length of time that the contract was active before
// renewal.
// the cost per block is calculated based on the total spent over
// the length of time that the contract was active before renewal.
oldContractSpent := contract.TotalCost.Sub(contract.ContractFee).Sub(contract.TxnFee).Sub(contract.SiafundFee).Sub(contract.RenterFunds)
oldContractLength := blockHeight - contract.StartHeight
if oldContractLength == 0 {
......@@ -435,8 +434,7 @@ func (c *Contractor) threadedContractMaintenance() {
spentPerBlock := oldContractSpent.Div64(uint64(oldContractLength))
renewAmount := spentPerBlock.Mul64(uint64(allowance.Period))
// Get an estimate for how much the fees will cost.
// Txn Fee
// Get an estimate for how much the fees will cost. Txn Fee
_, maxTxnFee := c.tpool.FeeEstimation()
// SiafundFee
......@@ -452,8 +450,8 @@ func (c *Contractor) threadedContractMaintenance() {
estimatedFees := host.ContractPrice.Add(maxTxnFee).Add(siafundFee)
renewAmount = renewAmount.Add(estimatedFees)
// Determine if there is enough funds available to supplement
// with a 33% bonus, and if there is, add a 33% bonus.
// Determine if there is enough funds available to supplement with a
// 33% bonus, and if there is, add a 33% bonus.
moneyBuffer := renewAmount.Div64(3)
if moneyBuffer.Cmp(fundsAvailable) < 0 {
renewAmount = renewAmount.Add(moneyBuffer)
......@@ -462,8 +460,8 @@ func (c *Contractor) threadedContractMaintenance() {
c.log.Println("WARN: performing a limited renew due to low allowance")
}
// The contract needs to be renewed because it is going to
// expire soon, and we need to refresh the time.
// The contract needs to be renewed because it is going to expire
// soon, and we need to refresh the time.
renewSet = append(renewSet, renewal{
id: contract.ID,
amount: renewAmount,
......@@ -474,8 +472,8 @@ func (c *Contractor) threadedContractMaintenance() {
host, _ := c.hdb.Host(contract.HostPublicKey)
// Skip this host if its prices are too high.
// managedMarkContractsUtility should make this redundant, but
// this is here for extra safety.
// managedMarkContractsUtility should make this redundant, but this
// is here for extra safety.
if host.StoragePrice.Cmp(maxStoragePrice) > 0 || host.UploadBandwidthPrice.Cmp(maxUploadPrice) > 0 {
continue
}
......@@ -486,9 +484,9 @@ func (c *Contractor) threadedContractMaintenance() {
sectorPrice := sectorStoragePrice.Add(sectorBandwidthPrice)
percentRemaining, _ := big.NewRat(0, 1).SetFrac(contract.RenterFunds.Big(), contract.TotalCost.Big()).Float64()
if contract.RenterFunds.Cmp(sectorPrice.Mul64(3)) < 0 || percentRemaining < minContractFundRenewalThreshold {
// This contract does need to be refreshed. Make sure there
// are enough funds available to perform the refresh, and
// then execute.
// This contract does need to be refreshed. Make sure there are
// enough funds available to perform the refresh, and then
// execute.
oldDuration := blockHeight - contract.StartHeight
newDuration := endHeight - blockHeight
spendPerBlock := contract.TotalCost.Div64(uint64(oldDuration))
......
......@@ -110,10 +110,10 @@ type hostContractor interface {
// Close closes the hostContractor.
Close() error
// Contracts returns the active contracts formed by the contractor.
// Contracts returns the staticContracts of the renter's hostContractor.
Contracts() []modules.RenterContract
// Contracts returns the old contracts formed by the contractor.
// OldContracts returns the oldContracts of the renter's hostContractor.
OldContracts() []modules.RenterContract
// ContractByPublicKey returns the contract associated with the host key.
......@@ -388,11 +388,13 @@ func (r *Renter) EstimateHostScore(e modules.HostDBEntry) modules.HostScoreBreak
return r.hostDB.EstimateHostScore(e)
}
// Contracts returns an array of host contractor's active contracts
// Contracts returns an array of host contractor's staticContracts
func (r *Renter) Contracts() []modules.RenterContract { return r.hostContractor.Contracts() }
// OldContracts returns an array of host contractor's old contracts
func (r *Renter) OldContracts() []modules.RenterContract { return r.hostContractor.OldContracts() }
// OldContracts returns an array of host contractor's oldContracts
func (r *Renter) OldContracts() []modules.RenterContract {
return r.hostContractor.OldContracts()
}
// CurrentPeriod returns the host contractor's current period
func (r *Renter) CurrentPeriod() types.BlockHeight { return r.hostContractor.CurrentPeriod() }
......
......@@ -11,12 +11,29 @@ import (
"github.com/NebulousLabs/Sia/node/api"
)
// RenterContractsGet requests the /renter/contracts resource
// RenterContractsGet requests the /renter/contracts resource and returns
// Contracts and ActiveContracts
func (c *Client) RenterContractsGet() (rc api.RenterContracts, err error) {
err = c.get("/renter/contracts", &rc)
return
}
// RenterInactiveContractsGet requests the /renter/contracts resource with the
// inactive flag set to true
func (c *Client) RenterInactiveContractsGet() (rc api.RenterContracts, err error) {
query := fmt.Sprintf("?inactive=%v", true)
err = c.get("/renter/contracts"+query, &rc)
return
}
// RenterExpiredContractsGet requests the /renter/contracts resource with the
// expired flag set to true
func (c *Client) RenterExpiredContractsGet() (rc api.RenterContracts, err error) {
query := fmt.Sprintf("?expired=%v", true)
err = c.get("/renter/contracts"+query, &rc)
return
}
// RenterDeletePost uses the /renter/delete endpoint to delete a file.
func (c *Client) RenterDeletePost(siaPath string) (err error) {
siaPath = strings.TrimPrefix(siaPath, "/")
......
......@@ -115,8 +115,10 @@ type (
// RenterContracts contains the renter's contracts.
RenterContracts struct {
Contracts []RenterContract `json:"contracts"`
OldContracts []RenterContract `json:"oldcontracts"`
Contracts []RenterContract `json:"contracts"`
ActiveContracts []RenterContract `json:"activecontracts"`
InactiveContracts []RenterContract `json:"inactivecontracts"`
ExpiredContracts []RenterContract `json:"expiredcontracts"`
}
// RenterDownloadQueue contains the renter's download queue.
......@@ -273,9 +275,36 @@ func (api *API) renterHandlerPOST(w http.ResponseWriter, req *http.Request, _ ht
WriteSuccess(w)
}
// renterContractsHandler handles the API call to request the Renter's contracts.
func (api *API) renterContractsHandler(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
// renterContractsHandler handles the API call to request the Renter's
// contracts.
//
// Active contracts are contracts that the renter is actively using to store
// data and can upload, download, and renew
//
// Inactive contracts are contracts that are not currently being used by the
// renter because they are !goodForRenew, but have endheights that are in the
// future so could potentially become active again
//
// Expired contracts are contracts who's endheights are in the past
func (api *API) renterContractsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
// Parse flags
inactive, err := scanBool(req.FormValue("inactive"))
if err != nil {
return
}
expired, err := scanBool(req.FormValue("expired"))
if err != nil {
return
}
// Get current block height for reference
blockHeight := api.cs.Height()
// Get active contracts
contracts := []RenterContract{}
activeContracts := []RenterContract{}
inactiveContracts := []RenterContract{}
expiredContracts := []RenterContract{}
for _, c := range api.renter.Contracts() {
var size uint64
if len(c.Transaction.FileContractRevisions) != 0 {
......@@ -296,8 +325,7 @@ func (api *API) renterContractsHandler(w http.ResponseWriter, _ *http.Request, _
goodForUpload = utility.GoodForUpload
goodForRenew = utility.GoodForRenew
}
contracts = append(contracts, RenterContract{
contract := RenterContract{
DownloadSpending: c.DownloadSpending,
EndHeight: c.EndHeight,
Fees: c.TxnFee.Add(c.SiafundFee).Add(c.ContractFee),
......@@ -314,52 +342,69 @@ func (api *API) renterContractsHandler(w http.ResponseWriter, _ *http.Request, _
StorageSpendingDeprecated: c.StorageSpending,
TotalCost: c.TotalCost,
UploadSpending: c.UploadSpending,
})
}
oldContracts := []RenterContract{}
for _, c := range api.renter.OldContracts() {
var size uint64
if len(c.Transaction.FileContractRevisions) != 0 {
size = c.Transaction.FileContractRevisions[0].NewFileSize
}
// Fetch host address
var netAddress modules.NetAddress
hdbe, exists := api.renter.Host(c.HostPublicKey)
if exists {
netAddress = hdbe.NetAddress
if goodForRenew {
activeContracts = append(activeContracts, contract)
} else if inactive && !goodForRenew {
inactiveContracts = append(inactiveContracts, contract)
}
// Fetch utilities for contract
var goodForUpload bool
var goodForRenew bool
if utility, ok := api.renter.ContractUtility(c.HostPublicKey); ok {
goodForUpload = utility.GoodForUpload
goodForRenew = utility.GoodForRenew
contracts = append(contracts, contract)
}
// Get expired contracts
if expired || inactive {
for _, c := range api.renter.OldContracts() {
var size uint64
if len(c.Transaction.FileContractRevisions) != 0 {
size = c.Transaction.FileContractRevisions[0].NewFileSize
}
// Fetch host address
var netAddress modules.NetAddress
hdbe, exists := api.renter.Host(c.HostPublicKey)
if exists {
netAddress = hdbe.NetAddress
}
// Fetch utilities for contract
var goodForUpload bool
var goodForRenew bool
if utility, ok := api.renter.ContractUtility(c.HostPublicKey); ok {
goodForUpload = utility.GoodForUpload
goodForRenew = utility.GoodForRenew
}
contract := RenterContract{
DownloadSpending: c.DownloadSpending,
EndHeight: c.EndHeight,
Fees: c.TxnFee.Add(c.SiafundFee).Add(c.ContractFee),
GoodForUpload: goodForUpload,
GoodForRenew: goodForRenew,
HostPublicKey: c.HostPublicKey,
ID: c.ID,
LastTransaction: c.Transaction,
NetAddress: netAddress,
RenterFunds: c.RenterFunds,
Size: size,
StartHeight: c.StartHeight,
StorageSpending: c.StorageSpending,
StorageSpendingDeprecated: c.StorageSpending,
TotalCost: c.TotalCost,
UploadSpending: c.UploadSpending,
}
if expired && c.EndHeight < blockHeight {
expiredContracts = append(expiredContracts, contract)
} else if inactive && c.EndHeight >= blockHeight {
inactiveContracts = append(inactiveContracts, contract)
}
}
oldContracts = append(oldContracts, RenterContract{
DownloadSpending: c.DownloadSpending,
EndHeight: c.EndHeight,
Fees: c.TxnFee.Add(c.SiafundFee).Add(c.ContractFee),
GoodForUpload: goodForUpload,