github.com/dmmcquay/sia@v1.3.1-0.20180712220038-9f8d535311b9/cmd/siac/hostdbcmd.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "math/big" 6 "os" 7 "text/tabwriter" 8 9 "github.com/spf13/cobra" 10 11 "github.com/NebulousLabs/Sia/modules" 12 "github.com/NebulousLabs/Sia/node/api" 13 "github.com/NebulousLabs/Sia/types" 14 ) 15 16 const scanHistoryLen = 30 17 18 var ( 19 hostdbNumHosts int 20 hostdbVerbose bool 21 ) 22 23 var ( 24 hostdbCmd = &cobra.Command{ 25 Use: "hostdb", 26 Short: "Interact with the renter's host database.", 27 Long: "View the list of active hosts, the list of all hosts, or query specific hosts.\nIf the '-v' flag is set, a list of recent scans will be provided, with the most\nrecent scan on the right. a '0' indicates that the host was offline, and a '1'\nindicates that the host was online.", 28 Run: wrap(hostdbcmd), 29 } 30 31 hostdbViewCmd = &cobra.Command{ 32 Use: "view [pubkey]", 33 Short: "View the full information for a host.", 34 Long: "View detailed information about a host, including things like a score breakdown.", 35 Run: wrap(hostdbviewcmd), 36 } 37 ) 38 39 // printScoreBreakdown prints the score breakdown of a host, provided the info. 40 func printScoreBreakdown(info *api.HostdbHostsGET) { 41 fmt.Println("\n Score Breakdown:") 42 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 43 fmt.Fprintf(w, "\t\tAge:\t %.3f\n", info.ScoreBreakdown.AgeAdjustment) 44 fmt.Fprintf(w, "\t\tBurn:\t %.3f\n", info.ScoreBreakdown.BurnAdjustment) 45 fmt.Fprintf(w, "\t\tCollateral:\t %.3f\n", info.ScoreBreakdown.CollateralAdjustment) 46 fmt.Fprintf(w, "\t\tInteraction:\t %.3f\n", info.ScoreBreakdown.InteractionAdjustment) 47 fmt.Fprintf(w, "\t\tPrice:\t %.3f\n", info.ScoreBreakdown.PriceAdjustment*1e6) 48 fmt.Fprintf(w, "\t\tStorage:\t %.3f\n", info.ScoreBreakdown.StorageRemainingAdjustment) 49 fmt.Fprintf(w, "\t\tUptime:\t %.3f\n", info.ScoreBreakdown.UptimeAdjustment) 50 fmt.Fprintf(w, "\t\tVersion:\t %.3f\n", info.ScoreBreakdown.VersionAdjustment) 51 w.Flush() 52 } 53 54 func hostdbcmd() { 55 if !hostdbVerbose { 56 info, err := httpClient.HostDbActiveGet() 57 if err != nil { 58 die("Could not fetch host list:", err) 59 } 60 if len(info.Hosts) == 0 { 61 fmt.Println("No known active hosts") 62 return 63 } 64 65 // Strip down to the number of requested hosts. 66 if hostdbNumHosts != 0 && hostdbNumHosts < len(info.Hosts) { 67 info.Hosts = info.Hosts[len(info.Hosts)-hostdbNumHosts:] 68 } 69 70 fmt.Println(len(info.Hosts), "Active Hosts:") 71 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 72 fmt.Fprintln(w, "\t\tAddress\tPrice (per TB per Mo)") 73 for i, host := range info.Hosts { 74 price := host.StoragePrice.Mul(modules.BlockBytesPerMonthTerabyte) 75 fmt.Fprintf(w, "\t%v:\t%v\t%v\n", len(info.Hosts)-i, host.NetAddress, currencyUnits(price)) 76 } 77 w.Flush() 78 } else { 79 info, err := httpClient.HostDbAllGet() 80 if err != nil { 81 die("Could not fetch host list:", err) 82 } 83 if len(info.Hosts) == 0 { 84 fmt.Println("No known hosts") 85 return 86 } 87 88 // Iterate through the hosts and divide by category. 89 var activeHosts, inactiveHosts, offlineHosts []api.ExtendedHostDBEntry 90 for _, host := range info.Hosts { 91 if host.AcceptingContracts && len(host.ScanHistory) > 0 && host.ScanHistory[len(host.ScanHistory)-1].Success { 92 activeHosts = append(activeHosts, host) 93 continue 94 } 95 if len(host.ScanHistory) > 0 && host.ScanHistory[len(host.ScanHistory)-1].Success { 96 inactiveHosts = append(inactiveHosts, host) 97 continue 98 } 99 offlineHosts = append(offlineHosts, host) 100 } 101 102 if hostdbNumHosts > 0 && len(offlineHosts) > hostdbNumHosts { 103 offlineHosts = offlineHosts[len(offlineHosts)-hostdbNumHosts:] 104 } 105 if hostdbNumHosts > 0 && len(inactiveHosts) > hostdbNumHosts { 106 inactiveHosts = inactiveHosts[len(inactiveHosts)-hostdbNumHosts:] 107 } 108 if hostdbNumHosts > 0 && len(activeHosts) > hostdbNumHosts { 109 activeHosts = activeHosts[len(activeHosts)-hostdbNumHosts:] 110 } 111 112 fmt.Println() 113 fmt.Println(len(offlineHosts), "Offline Hosts:") 114 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 115 fmt.Fprintln(w, "\t\tPubkey\tAddress\tPrice (/ TB / Month)\tDownload Price (/ TB)\tUptime\tRecent Scans") 116 for i, host := range offlineHosts { 117 // Compute the total measured uptime and total measured downtime for this 118 // host. 119 uptimeRatio := float64(0) 120 if len(host.ScanHistory) > 1 { 121 downtime := host.HistoricDowntime 122 uptime := host.HistoricUptime 123 recentTime := host.ScanHistory[0].Timestamp 124 recentSuccess := host.ScanHistory[0].Success 125 for _, scan := range host.ScanHistory[1:] { 126 if recentSuccess { 127 uptime += scan.Timestamp.Sub(recentTime) 128 } else { 129 downtime += scan.Timestamp.Sub(recentTime) 130 } 131 recentTime = scan.Timestamp 132 recentSuccess = scan.Success 133 } 134 uptimeRatio = float64(uptime) / float64(uptime+downtime) 135 } 136 137 // Get the scan history string. 138 scanHistStr := "" 139 displayScans := host.ScanHistory 140 if len(host.ScanHistory) > scanHistoryLen { 141 displayScans = host.ScanHistory[len(host.ScanHistory)-scanHistoryLen:] 142 } 143 for _, scan := range displayScans { 144 if scan.Success { 145 scanHistStr += "1" 146 } else { 147 scanHistStr += "0" 148 } 149 } 150 151 // Get a string representation of the historic outcomes of the most 152 // recent scans. 153 price := host.StoragePrice.Mul(modules.BlockBytesPerMonthTerabyte) 154 downloadBWPrice := host.StoragePrice.Mul(modules.BytesPerTerabyte) 155 fmt.Fprintf(w, "\t%v:\t%v\t%v\t%v\t%v\t%.3f\t%s\n", len(offlineHosts)-i, host.PublicKeyString, 156 host.NetAddress, currencyUnits(price), currencyUnits(downloadBWPrice), uptimeRatio, scanHistStr) 157 } 158 w.Flush() 159 160 fmt.Println() 161 fmt.Println(len(inactiveHosts), "Inactive Hosts:") 162 w = tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 163 fmt.Fprintln(w, "\t\tPubkey\tAddress\tPrice (/ TB / Month)\tDownload Price (/ TB)\tUptime\tRecent Scans") 164 for i, host := range inactiveHosts { 165 // Compute the total measured uptime and total measured downtime for this 166 // host. 167 uptimeRatio := float64(0) 168 if len(host.ScanHistory) > 1 { 169 downtime := host.HistoricDowntime 170 uptime := host.HistoricUptime 171 recentTime := host.ScanHistory[0].Timestamp 172 recentSuccess := host.ScanHistory[0].Success 173 for _, scan := range host.ScanHistory[1:] { 174 if recentSuccess { 175 uptime += scan.Timestamp.Sub(recentTime) 176 } else { 177 downtime += scan.Timestamp.Sub(recentTime) 178 } 179 recentTime = scan.Timestamp 180 recentSuccess = scan.Success 181 } 182 uptimeRatio = float64(uptime) / float64(uptime+downtime) 183 } 184 185 // Get a string representation of the historic outcomes of the most 186 // recent scans. 187 scanHistStr := "" 188 displayScans := host.ScanHistory 189 if len(host.ScanHistory) > scanHistoryLen { 190 displayScans = host.ScanHistory[len(host.ScanHistory)-scanHistoryLen:] 191 } 192 for _, scan := range displayScans { 193 if scan.Success { 194 scanHistStr += "1" 195 } else { 196 scanHistStr += "0" 197 } 198 } 199 200 price := host.StoragePrice.Mul(modules.BlockBytesPerMonthTerabyte) 201 downloadBWPrice := host.DownloadBandwidthPrice.Mul(modules.BytesPerTerabyte) 202 fmt.Fprintf(w, "\t%v:\t%v\t%v\t%v\t%v\t%.3f\t%s\n", len(inactiveHosts)-i, host.PublicKeyString, host.NetAddress, currencyUnits(price), currencyUnits(downloadBWPrice), uptimeRatio, scanHistStr) 203 } 204 w.Flush() 205 206 // Grab the host at the 1/5th point and use it as the reference. (it's 207 // like using the median, except at the 1/5th point instead of the 1/2 208 // point.) 209 referenceScore := big.NewRat(1, 1) 210 if len(activeHosts) > 0 { 211 referenceIndex := len(activeHosts) / 5 212 hostInfo, err := httpClient.HostDbHostsGet(activeHosts[referenceIndex].PublicKey) 213 if err != nil { 214 die("Could not fetch provided host:", err) 215 } 216 if !hostInfo.ScoreBreakdown.Score.IsZero() { 217 referenceScore = new(big.Rat).Inv(new(big.Rat).SetInt(hostInfo.ScoreBreakdown.Score.Big())) 218 } 219 } 220 221 fmt.Println() 222 fmt.Println(len(activeHosts), "Active Hosts:") 223 w = tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 224 fmt.Fprintln(w, "\t\tPubkey\tAddress\tScore\tPrice (/ TB / Month)\tDownload Price (/TB)\tUptime\tRecent Scans") 225 for i, host := range activeHosts { 226 // Compute the total measured uptime and total measured downtime for this 227 // host. 228 uptimeRatio := float64(0) 229 if len(host.ScanHistory) > 1 { 230 downtime := host.HistoricDowntime 231 uptime := host.HistoricUptime 232 recentTime := host.ScanHistory[0].Timestamp 233 recentSuccess := host.ScanHistory[0].Success 234 for _, scan := range host.ScanHistory[1:] { 235 if recentSuccess { 236 uptime += scan.Timestamp.Sub(recentTime) 237 } else { 238 downtime += scan.Timestamp.Sub(recentTime) 239 } 240 recentTime = scan.Timestamp 241 recentSuccess = scan.Success 242 } 243 uptimeRatio = float64(uptime) / float64(uptime+downtime) 244 } 245 246 // Get a string representation of the historic outcomes of the most 247 // recent scans. 248 scanHistStr := "" 249 displayScans := host.ScanHistory 250 if len(host.ScanHistory) > scanHistoryLen { 251 displayScans = host.ScanHistory[len(host.ScanHistory)-scanHistoryLen:] 252 } 253 for _, scan := range displayScans { 254 if scan.Success { 255 scanHistStr += "1" 256 } else { 257 scanHistStr += "0" 258 } 259 } 260 261 // Grab the score information for the active hosts. 262 hostInfo, err := httpClient.HostDbHostsGet(host.PublicKey) 263 if err != nil { 264 die("Could not fetch provided host:", err) 265 } 266 score, _ := new(big.Rat).Mul(referenceScore, new(big.Rat).SetInt(hostInfo.ScoreBreakdown.Score.Big())).Float64() 267 268 price := host.StoragePrice.Mul(modules.BlockBytesPerMonthTerabyte) 269 downloadBWPrice := host.DownloadBandwidthPrice.Mul(modules.BytesPerTerabyte) 270 fmt.Fprintf(w, "\t%v:\t%v\t%v\t%12.6g\t%v\t%v\t%.3f\t%s\n", len(activeHosts)-i, host.PublicKeyString, host.NetAddress, score, currencyUnits(price), currencyUnits(downloadBWPrice), uptimeRatio, scanHistStr) 271 } 272 w.Flush() 273 } 274 } 275 276 func hostdbviewcmd(pubkey string) { 277 var publicKey types.SiaPublicKey 278 publicKey.LoadString(pubkey) 279 info, err := httpClient.HostDbHostsGet(publicKey) 280 if err != nil { 281 die("Could not fetch provided host:", err) 282 } 283 284 fmt.Println("Host information:") 285 286 fmt.Println(" Public Key:", info.Entry.PublicKeyString) 287 fmt.Println(" Block First Seen:", info.Entry.FirstSeen) 288 289 fmt.Println("\n Host Settings:") 290 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 291 fmt.Fprintln(w, "\t\tAccepting Contracts:\t", info.Entry.AcceptingContracts) 292 fmt.Fprintln(w, "\t\tTotal Storage:\t", info.Entry.TotalStorage/1e9, "GB") 293 fmt.Fprintln(w, "\t\tRemaining Storage:\t", info.Entry.RemainingStorage/1e9, "GB") 294 fmt.Fprintln(w, "\t\tOffered Collateral (TB / Mo):\t", currencyUnits(info.Entry.Collateral.Mul(modules.BlockBytesPerMonthTerabyte))) 295 fmt.Fprintln(w, "\n\t\tContract Price:\t", currencyUnits(info.Entry.ContractPrice)) 296 fmt.Fprintln(w, "\t\tStorage Price (TB / Mo):\t", currencyUnits(info.Entry.StoragePrice.Mul(modules.BlockBytesPerMonthTerabyte))) 297 fmt.Fprintln(w, "\t\tDownload Price (1 TB):\t", currencyUnits(info.Entry.DownloadBandwidthPrice.Mul(modules.BytesPerTerabyte))) 298 fmt.Fprintln(w, "\t\tUpload Price (1 TB):\t", currencyUnits(info.Entry.UploadBandwidthPrice.Mul(modules.BytesPerTerabyte))) 299 fmt.Fprintln(w, "\t\tVersion:\t", info.Entry.Version) 300 w.Flush() 301 302 printScoreBreakdown(&info) 303 304 // Compute the total measured uptime and total measured downtime for this 305 // host. 306 uptimeRatio := float64(0) 307 if len(info.Entry.ScanHistory) > 1 { 308 downtime := info.Entry.HistoricDowntime 309 uptime := info.Entry.HistoricUptime 310 recentTime := info.Entry.ScanHistory[0].Timestamp 311 recentSuccess := info.Entry.ScanHistory[0].Success 312 for _, scan := range info.Entry.ScanHistory[1:] { 313 if recentSuccess { 314 uptime += scan.Timestamp.Sub(recentTime) 315 } else { 316 downtime += scan.Timestamp.Sub(recentTime) 317 } 318 recentTime = scan.Timestamp 319 recentSuccess = scan.Success 320 } 321 uptimeRatio = float64(uptime) / float64(uptime+downtime) 322 } 323 324 // Compute the uptime ratio, but shift by 0.02 to acknowledge fully that 325 // 98% uptime and 100% uptime is valued the same. 326 fmt.Println("\n Scan History Length:", len(info.Entry.ScanHistory)) 327 fmt.Printf(" Overall Uptime: %.3f\n", uptimeRatio) 328 329 fmt.Println() 330 }