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  }