-
Notifications
You must be signed in to change notification settings - Fork 11
/
cost.go
174 lines (143 loc) · 7.54 KB
/
cost.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright 2019 DxChain, All rights reserved.
// Use of this source code is governed by an Apache
// License 2.0 that can be found in the LICENSE file
package contractmanager
import (
"github.com/DxChainNetwork/godx/common"
"github.com/DxChainNetwork/godx/storage"
)
// renewCostEstimation will estimate the estimated cost for the contract period after the renew, the cost estimation included the following costs:
// 1. storageCost for storing current amount of data (contract.LatestContractRevision.NewFileSize) for the current amount of time (period)
// 2. calculate the upload and download cost within the current period. Multiple renews may happen within the same current period when contract go renew
// due to lack of contract funding. At that time, the current period will not be updated
// 3. calculate the storage cost for the current period
// 4. get the contract cost from the host
// SUMMARY: totalStorageCost + upload cost within current period + download cost within the current period + storage cost within the current period
// + contract cost specified by the storage host -> raise 33%
func (cm *ContractManager) renewCostEstimation(host storage.HostInfo, contract storage.ContractMetaData, blockHeight uint64, rent storage.RentPayment) (estimation common.BigInt) {
// get the cost for current storage
amountDataStored := contract.LatestContractRevision.NewFileSize
storageCost := host.StoragePrice.MultUint64(rent.Period).MultUint64(amountDataStored)
// add all upload and download cost regarding to this contract
// NOTE: only within the current period
currentID := contract.ID
prevContractTotalUploadCost := contract.UploadCost
prevContractTotalDownloadCost := contract.DownloadCost
cm.lock.RLock()
// prevent loop from running forever
for i := 0; i < 10e5; i++ {
// get the previous contractID
prevContractID, exists := cm.renewedFrom[currentID]
if !exists {
break
}
// get the previous contract information
prevContract, exists := cm.expiredContracts[prevContractID]
if !exists {
break
}
// verify the start height to check if the start height is within the current period
// current period will be changed in two places:
// 1. SetRentPayment
// 2. ChainChanges
if prevContract.StartHeight < cm.currentPeriod {
break
}
// update the upload and download cost. Note: this cost is spent within the current period
prevContractTotalUploadCost = prevContractTotalUploadCost.Add(prevContract.UploadCost)
prevContractTotalDownloadCost = prevContractTotalDownloadCost.Add(prevContract.DownloadCost)
currentID = prevContractID
}
cm.lock.RUnlock()
// amount of data uploaded = total amount of data stored in the contract / uploadBandwidthPrice
prevDataUploaded := common.NewBigIntUint64(amountDataStored)
if host.UploadBandwidthPrice.Cmp(common.BigInt0) > 0 {
prevDataUploaded = prevContractTotalUploadCost.Div(host.UploadBandwidthPrice)
}
// if the data uploaded is greater than the total data stored in the contract
// this can only happen if the UploadBandwidthPrice is 0, therefore, the above
// validation is skipped
if prevDataUploaded.Cmp(common.NewBigIntUint64(amountDataStored)) > 0 {
prevDataUploaded = common.NewBigIntUint64(amountDataStored)
}
// upload cost + the storage cost for the newly uploaded data
newUploadCost := prevContractTotalUploadCost.Add(prevDataUploaded.MultUint64(rent.Period).Mult(host.StoragePrice))
// contract price will be stayed the same
contractPrice := host.ContractPrice
// TODO (mzhang): Gas fee estimation is ignored currently
// assume the download cost does not change for the new contract period
estimation = storageCost.Add(newUploadCost).Add(prevContractTotalDownloadCost).Add(contractPrice)
// rise the estimation up by 33%
estimation = estimation.Add(estimation.DivUint64(3))
// calculate the minimum rentPayment fund for the contract
minRentFund := rent.Fund.MultFloat64(minContractPaymentFactor).DivUint64(rent.StorageHosts)
if estimation.Cmp(minRentFund) < 0 {
estimation = minRentFund
}
return
}
// CalculatePeriodCost will calculate the storage client's cost for one period (including all contracts)
func (cm *ContractManager) CalculatePeriodCost(rentPayment storage.RentPayment) (periodCost storage.PeriodCost) {
// get all activeContracts
activeContracts := cm.activeContracts.RetrieveAllContractsMetaData()
cm.lock.RLock()
defer cm.lock.RUnlock()
// loop through all signed contract, get the total cost spent
// by the storage client
for _, contract := range activeContracts {
updatePrevContractCost(&periodCost, contract)
}
// loop through expired contracts list, update the money spent by the storage client
// for expiredContracts, only contract within the current period will be used to do the
// PeriodCost calculation
for _, contract := range cm.expiredContracts {
host, exists := cm.hostManager.RetrieveHostInfo(contract.EnodeID)
// it is possible that the expiredContract (got renewed but still not expired, old contract)
// started within the current period. Therefore, add cost for that contract as well
if contract.StartHeight >= cm.currentPeriod {
updatePrevContractCost(&periodCost, contract)
} else if exists && contract.EndHeight+host.WindowSize+maturityDelay > cm.blockHeight {
// if the host exists, and the contract is still waiting for the storage proof
// then it means the balance left in the contract is still withHeld and not
// give back to the client yet
periodCost.WithheldFund = periodCost.WithheldFund.Add(contract.ContractBalance)
// update the withheldFundReleaseBlock to maximum block number
if contract.EndHeight+host.WindowSize+maturityDelay >= periodCost.WithheldFundReleaseBlock {
periodCost.WithheldFundReleaseBlock = contract.EndHeight + host.WindowSize + maturityDelay
}
// calculate the previous contract cost
calculatePrevContractCost(&periodCost, contract)
} else {
// calculate the previous contract cost directly
calculatePrevContractCost(&periodCost, contract)
}
}
// calculate the unspent fund based on the cost spent by the storage client already
calculateContractUnspentFund(&periodCost, rentPayment.Fund)
return
}
// calculateContractUnspentFund will be used to calculate the storage client's remaining contract fund
func calculateContractUnspentFund(pc *storage.PeriodCost, contractPayment common.BigInt) {
totalContractCost := pc.ContractFees.Add(pc.UploadCost).Add(pc.DownloadCost).Add(pc.StorageCost)
if contractPayment.Cmp(totalContractCost) > 0 {
pc.UnspentFund = contractPayment.Sub(totalContractCost)
}
}
// calculatePrevContractCost will be used to calculate the previous contract cost
func calculatePrevContractCost(pc *storage.PeriodCost, contract storage.ContractMetaData) {
pc.PrevContractCost = pc.PrevContractCost.Add(contract.ContractFee).Add(contract.GasCost).
Add(contract.UploadCost).Add(contract.DownloadCost).Add(contract.StorageCost)
}
// updatePrevContractCost will be used to update the previous contracts cost, which will be
// used to calculate the total storage client period cost
func updatePrevContractCost(pc *storage.PeriodCost, contract storage.ContractMetaData) {
// calculate the contract fees
pc.ContractFees = pc.ContractFees.Add(contract.ContractFee)
pc.ContractFees = pc.ContractFees.Add(contract.GasCost)
// calculate the upload, download, and storage cost
pc.UploadCost = pc.UploadCost.Add(contract.UploadCost)
pc.DownloadCost = pc.DownloadCost.Add(contract.DownloadCost)
pc.StorageCost = pc.StorageCost.Add(contract.StorageCost)
// get the total contract available payment
pc.ContractFund = pc.ContractFund.Add(contract.TotalCost)
}