/
rentpayment.go
154 lines (131 loc) · 5.04 KB
/
rentpayment.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
// 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 (
"errors"
"fmt"
"reflect"
"github.com/DxChainNetwork/godx/common/unit"
"github.com/DxChainNetwork/godx/storage"
)
var (
// erasure code related parameters. Default to params set in storage module.
// The values are redeclared here only for test cases
defaultMinSectors = storage.DefaultMinSectors
defaultNumSectors = storage.DefaultNumSectors
)
// hostMarket is the interface implemented by storageHostManager
type hostMarket interface {
GetMarketPrice() storage.MarketPrice
}
// SetRentPayment will set the rent payment to the value passed in by the user
// through the command line interface
func (cm *ContractManager) SetRentPayment(rent storage.RentPayment, market hostMarket) (err error) {
if cm.b.Syncing() {
return errors.New("setRentPayment can only be done once the block chain finished syncing")
}
// Calculate the expected sizes in rent payment.
prices := market.GetMarketPrice()
rent = estimateRentPaymentSizes(rent, prices)
// validate the rentPayment, making sure that fields are not empty
if err = RentPaymentValidation(rent); err != nil {
return
}
// getting the old payment
cm.lock.Lock()
oldCurrentPeriod := cm.currentPeriod
oldRent := cm.rentPayment
cm.rentPayment = rent
cm.lock.Unlock()
// if error is not nil, revert the settings back to the
// original settings
defer func() {
if err != nil {
cm.lock.Lock()
cm.rentPayment = oldRent
cm.currentPeriod = oldCurrentPeriod
cm.lock.Unlock()
}
}()
// indicates the contracts have been canceled previously
// or it is client's first time signing the storage contract
if reflect.DeepEqual(oldRent, storage.RentPayment{}) {
// update the current period
cm.lock.Lock()
cm.currentPeriod = cm.blockHeight
cm.lock.Unlock()
// reuse the active canceled contracts
if err = cm.resumeContracts(); err != nil {
cm.log.Error("SetRentPayment failed, error resuming the active storage contracts", "err", err.Error())
return
}
}
// update storage host manager rentPayment payment
// which updates the storage host evaluation function
if err = cm.hostManager.SetRentPayment(rent); err != nil {
cm.log.Error("SetRentPayment failed, failed to set the rent payment for host manager", "err", err.Error())
return
}
// save all the settings
if err = cm.saveSettings(); err != nil {
cm.log.Error("SetRentPayment failed, unable to save settings while setting the rent payment", "err", err.Error())
// set the storage host's rentPayment back to original value
_ = cm.hostManager.SetRentPayment(oldRent)
return fmt.Errorf("failed to save settings persistently: %s", err.Error())
}
// if the maintenance process is running, stop it
cm.lock.Lock()
if cm.maintenanceRunning {
cm.maintenanceStop <- struct{}{}
}
cm.lock.Unlock()
// wait util the current maintenance finished execution, and start new maintenance
go func() {
cm.maintenanceWg.Wait()
cm.contractMaintenance()
}()
return
}
// AcquireRentPayment will return the RentPayment settings
func (cm *ContractManager) AcquireRentPayment() (rentPayment storage.RentPayment) {
return cm.rentPayment
}
// RentPaymentValidation will validate the rentPayment. All fields must be
// non-zero value
func RentPaymentValidation(rent storage.RentPayment) (err error) {
switch {
case rent.StorageHosts == 0:
return errors.New("amount of storage hosts cannot be set to 0")
case rent.Period == 0:
return errors.New("storage period cannot be set to 0")
case storage.RenewWindow > rent.Period:
return fmt.Errorf("storage period must be greater than %v", unit.FormatTime(storage.RenewWindow))
default:
return
}
}
// estimateRentPaymentSizes estimate the sizes in rent payment based on fund settings and the
// input market price. Currently, the contract fund are split among the storage fund, upload
// fund and download fund. The sizes follows the ratio defined in defaults.go
func estimateRentPaymentSizes(rent storage.RentPayment, prices storage.MarketPrice) storage.RentPayment {
// Estimate the redundancy
redundancy := float64(defaultNumSectors) / float64(defaultMinSectors)
// Estimate the sizes
fundPerContract := rent.Fund.DivUint64(rent.StorageHosts)
storageRatio := prices.StoragePrice.MultFloat64(redundancy).MultUint64(rent.Period).MultFloat64(storageSizeRatio)
downloadRatio := prices.DownloadPrice.MultFloat64(uploadSizeRatio)
uploadRatio := prices.UploadPrice.MultFloat64(downloadSizeRatio)
ratioSum := storageRatio.Add(downloadRatio).Add(uploadRatio)
// Calculate the sizes
sizeBase := fundPerContract.Div(ratioSum).Float64()
expectedStorage := uint64(sizeBase * storageSizeRatio * redundancy)
expectedDownload := uint64(sizeBase * downloadSizeRatio)
expectedUpload := uint64(sizeBase * uploadSizeRatio)
// Apply the calculated values to rent Payment
rent.ExpectedRedundancy = redundancy
rent.ExpectedStorage = expectedStorage
rent.ExpectedUpload = expectedUpload
rent.ExpectedDownload = expectedDownload
return rent
}