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  }