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