github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/cmd/spc/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 "SiaPrime/modules" 12 "SiaPrime/node/api" 13 "SiaPrime/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/1e27) 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)\tCollateral (/ 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 collateral := host.Collateral.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%v\t%.3f\t%s\n", len(inactiveHosts)-i, host.PublicKeyString, host.NetAddress, currencyUnits(price), currencyUnits(collateral), currencyUnits(downloadBWPrice), uptimeRatio, scanHistStr) 204 } 205 fmt.Fprintln(w, "\t\tPubkey\tAddress\tPrice (/ TB / Month)\tCollateral (/ TB / Month)\tDownload Price (/ TB)\tUptime\tRecent Scans") 206 w.Flush() 207 208 // Grab the host at the 3/5th point and use it as the reference. (it's 209 // like using the median, except at the 3/5th point instead of the 1/2 210 // point.) 211 referenceScore := big.NewRat(1, 1) 212 if len(activeHosts) > 0 { 213 referenceIndex := len(activeHosts) * 3 / 5 214 hostInfo, err := httpClient.HostDbHostsGet(activeHosts[referenceIndex].PublicKey) 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\tContract Fee\tPrice (/ TB / Month)\tCollateral (/ 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, err := httpClient.HostDbHostsGet(host.PublicKey) 265 if err != nil { 266 die("Could not fetch provided host:", err) 267 } 268 score, _ := new(big.Rat).Mul(referenceScore, new(big.Rat).SetInt(hostInfo.ScoreBreakdown.Score.Big())).Float64() 269 270 price := host.StoragePrice.Mul(modules.BlockBytesPerMonthTerabyte) 271 collateral := host.Collateral.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%v\t%v\t%.3f\t%s\n", len(activeHosts)-i, host.PublicKeyString, host.NetAddress, score, currencyUnits(host.ContractPrice), currencyUnits(price), currencyUnits(collateral), currencyUnits(downloadBWPrice), uptimeRatio, scanHistStr) 274 } 275 fmt.Fprintln(w, "\t\tPubkey\tAddress\tScore\tContract Fee\tPrice (/ TB / Month)\tCollateral (/ TB / Month)\tDownload Price (/TB)\tUptime\tRecent Scans") 276 w.Flush() 277 } 278 } 279 280 func hostdbviewcmd(pubkey string) { 281 var publicKey types.SiaPublicKey 282 publicKey.LoadString(pubkey) 283 info, err := httpClient.HostDbHostsGet(publicKey) 284 if err != nil { 285 die("Could not fetch provided host:", err) 286 } 287 288 fmt.Println("Host information:") 289 290 fmt.Println(" Public Key:", info.Entry.PublicKeyString) 291 fmt.Println(" Block First Seen:", info.Entry.FirstSeen) 292 fmt.Println(" Absolute Score:", info.ScoreBreakdown.Score) 293 294 fmt.Println("\n Host Settings:") 295 w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) 296 fmt.Fprintln(w, "\t\tAccepting Contracts:\t", info.Entry.AcceptingContracts) 297 fmt.Fprintln(w, "\t\tTotal Storage:\t", info.Entry.TotalStorage/1e9, "GB") 298 fmt.Fprintln(w, "\t\tRemaining Storage:\t", info.Entry.RemainingStorage/1e9, "GB") 299 fmt.Fprintln(w, "\t\tOffered Collateral (TB / Mo):\t", currencyUnits(info.Entry.Collateral.Mul(modules.BlockBytesPerMonthTerabyte))) 300 fmt.Fprintln(w, "\n\t\tContract Price:\t", currencyUnits(info.Entry.ContractPrice)) 301 fmt.Fprintln(w, "\t\tStorage Price (TB / Mo):\t", currencyUnits(info.Entry.StoragePrice.Mul(modules.BlockBytesPerMonthTerabyte))) 302 fmt.Fprintln(w, "\t\tDownload Price (1 TB):\t", currencyUnits(info.Entry.DownloadBandwidthPrice.Mul(modules.BytesPerTerabyte))) 303 fmt.Fprintln(w, "\t\tUpload Price (1 TB):\t", currencyUnits(info.Entry.UploadBandwidthPrice.Mul(modules.BytesPerTerabyte))) 304 fmt.Fprintln(w, "\t\tVersion:\t", info.Entry.Version) 305 w.Flush() 306 307 printScoreBreakdown(&info) 308 309 // Compute the total measured uptime and total measured downtime for this 310 // host. 311 uptimeRatio := float64(0) 312 if len(info.Entry.ScanHistory) > 1 { 313 downtime := info.Entry.HistoricDowntime 314 uptime := info.Entry.HistoricUptime 315 recentTime := info.Entry.ScanHistory[0].Timestamp 316 recentSuccess := info.Entry.ScanHistory[0].Success 317 for _, scan := range info.Entry.ScanHistory[1:] { 318 if recentSuccess { 319 uptime += scan.Timestamp.Sub(recentTime) 320 } else { 321 downtime += scan.Timestamp.Sub(recentTime) 322 } 323 recentTime = scan.Timestamp 324 recentSuccess = scan.Success 325 } 326 uptimeRatio = float64(uptime) / float64(uptime+downtime) 327 } 328 329 // Compute the uptime ratio, but shift by 0.02 to acknowledge fully that 330 // 98% uptime and 100% uptime is valued the same. 331 fmt.Println("\n Scan History Length:", len(info.Entry.ScanHistory)) 332 fmt.Printf(" Overall Uptime: %.3f\n", uptimeRatio) 333 334 fmt.Println() 335 }