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  }