func TestCurrencyUnits(t *testing.T) { tests := []struct { in, out string }{ {"1", "1 H"}, {"1000", "1000 H"}, {"100000000000", "100000000000 H"}, {"1000000000000", "1 pS"}, {"1234560000000", "1.235 pS"}, {"12345600000000", "12.35 pS"}, {"123456000000000", "123.5 pS"}, {"1000000000000000", "1 nS"}, {"1000000000000000000", "1 uS"}, {"1000000000000000000000", "1 mS"}, {"1000000000000000000000000", "1 SC"}, {"1000000000000000000000000000", "1 KS"}, {"1000000000000000000000000000000", "1 MS"}, {"1000000000000000000000000000000000", "1 GS"}, {"1000000000000000000000000000000000000", "1 TS"}, {"1234560000000000000000000000000000000", "1.235 TS"}, {"1234560000000000000000000000000000000000", "1235 TS"}, } for _, test := range tests { i, _ := new(big.Int).SetString(test.in, 10) out := currencyUnits(types.NewCurrency(i)) if out != test.out { t.Errorf("currencyUnits(%v): expected %v, got %v", test.in, test.out, out) } } }
// RandomHosts will pull up to 'num' random hosts from the hostdb. There will // be no repeats, but the length of the slice returned may be less than 'num', // and may even be 0. The hosts that get returned first have the higher // priority. func (hdb *HostDB) RandomHosts(count int) (hosts []modules.HostSettings) { id := hdb.mu.Lock() defer hdb.mu.Unlock(id) var removedEntries []*hostEntry for len(hosts) < count { if hdb.hostTree == nil || hdb.hostTree.weight.IsZero() { break } randWeight, err := rand.Int(rand.Reader, hdb.hostTree.weight.Big()) if err != nil { break } node, err := hdb.hostTree.nodeAtWeight(types.NewCurrency(randWeight)) if err != nil { break } hosts = append(hosts, node.hostEntry.HostSettings) node.removeNode() delete(hdb.activeHosts, node.hostEntry.IPAddress) // remove the entry from the hostdb so it won't be selected as a // repeat. removedEntries = append(removedEntries, node.hostEntry) } // Add back all of the entries that got removed. for _, entry := range removedEntries { hdb.insertNode(entry) } return hosts }
// scanAmount scans a types.Currency from a string. func scanAmount(amount string) (types.Currency, bool) { // use SetString manually to ensure that amount does not contain // multiple values, which would confuse fmt.Scan i, ok := new(big.Int).SetString(amount, 10) if !ok { return types.Currency{}, ok } return types.NewCurrency(i), true }
// hostconfigcmd is the handler for the command `siac host config [setting] [value]`. // Modifies host settings. func hostconfigcmd(param, value string) { switch param { // currency (convert to hastings) case "collateralbudget", "maxcollateral", "mincontractprice": hastings, err := parseCurrency(value) if err != nil { die("Could not parse "+param+":", err) } value = hastings // currency/TB (convert to hastings/byte) case "mindownloadbandwidthprice", "minuploadbandwidthprice": hastings, err := parseCurrency(value) if err != nil { die("Could not parse "+param+":", err) } i, _ := new(big.Int).SetString(hastings, 10) c := types.NewCurrency(i).Div(modules.BytesPerTerabyte) value = c.String() // currency/TB/month (convert to hastings/byte/block) case "collateral", "minstorageprice": hastings, err := parseCurrency(value) if err != nil { die("Could not parse "+param+":", err) } i, _ := new(big.Int).SetString(hastings, 10) c := types.NewCurrency(i).Div(modules.BlockBytesPerMonthTerabyte) value = c.String() // other valid settings case "acceptingcontracts", "maxdownloadbatchsize", "maxduration", "maxrevisebatchsize", "netaddress", "windowsize": // invalid settings default: die("\"" + param + "\" is not a host setting") } err := post("/host", param+"="+value) if err != nil { die("Could not update host settings:", err) } fmt.Println("Host settings updated.") }
// Returns many pieces of readily available information func (e *Explorer) Statistics() modules.ExplorerStatistics { e.mu.RLock() defer e.mu.RUnlock() target, _ := e.cs.ChildTarget(e.currentBlock) difficulty := types.NewCurrency(types.RootTarget.Int()).Div(types.NewCurrency(target.Int())) currentBlock, exists := e.cs.BlockAtHeight(e.blockchainHeight) if build.DEBUG && !exists { panic("current block not found in consensus set") } return modules.ExplorerStatistics{ Height: e.blockchainHeight, CurrentBlock: e.currentBlock, Target: target, Difficulty: difficulty, MaturityTimestamp: currentBlock.Timestamp, TotalCoins: types.CalculateNumSiacoins(e.blockchainHeight), MinerPayoutCount: e.minerPayoutCount, TransactionCount: e.transactionCount, SiacoinInputCount: e.siacoinInputCount, SiacoinOutputCount: e.siacoinOutputCount, FileContractCount: e.fileContractCount, FileContractRevisionCount: e.fileContractRevisionCount, StorageProofCount: e.storageProofCount, SiafundInputCount: e.siafundInputCount, SiafundOutputCount: e.siafundOutputCount, MinerFeeCount: e.minerFeeCount, ArbitraryDataCount: e.arbitraryDataCount, TransactionSignatureCount: e.transactionSignatureCount, ActiveContractCount: e.activeContractCount, ActiveContractCost: e.activeContractCost, ActiveContractSize: e.activeContractSize, TotalContractCost: e.totalContractCost, TotalContractSize: e.totalContractSize, } }
// RandomHosts will pull up to 'n' random hosts from the hostdb. There will be // no repeats, but the length of the slice returned may be less than 'n', and // may even be 0. The hosts that get returned first have the higher priority. // Hosts specified in 'ignore' will not be considered; pass 'nil' if no // blacklist is desired. func (hdb *HostDB) RandomHosts(n int, ignore []modules.NetAddress) (hosts []modules.HostDBEntry) { hdb.mu.Lock() defer hdb.mu.Unlock() if hdb.isEmpty() { return } // These will be restored after selection is finished. var removedEntries []*hostEntry // Remove hosts that we want to ignore. for _, addr := range ignore { node, exists := hdb.activeHosts[addr] if !exists { continue } node.removeNode() delete(hdb.activeHosts, addr) removedEntries = append(removedEntries, node.hostEntry) } // Pick a host, remove it from the tree, and repeat until we have n hosts // or the tree is empty. for len(hosts) < n && !hdb.isEmpty() { randWeight, err := rand.Int(rand.Reader, hdb.hostTree.weight.Big()) if err != nil { build.Critical("rand.Int is returning an error:", err) break } node, err := hdb.hostTree.nodeAtWeight(types.NewCurrency(randWeight)) if err != nil { build.Critical("nodeAtWeight is returning and error:", err) break } // Only return the host if they are accepting contracts. if node.hostEntry.HostDBEntry.AcceptingContracts { hosts = append(hosts, node.hostEntry.HostDBEntry) } removedEntries = append(removedEntries, node.hostEntry) node.removeNode() delete(hdb.activeHosts, node.hostEntry.NetAddress) } // Add back all of the entries that got removed. for i := range removedEntries { hdb.insertNode(removedEntries[i]) } return hosts }
// randomHosts will pull up to 'n' random hosts from the hostdb. There will be // no repeats, but the length of the slice returned may be less than 'n', and // may even be 0. The hosts that get returned first have the higher priority. // Hosts specified in 'ignore' will not be considered; pass 'nil' if no // blacklist is desired. func (hdb *HostDB) randomHosts(n int, ignore []modules.NetAddress) (hosts []modules.HostSettings) { if hdb.isEmpty() { return } // These will be restored after selection is finished. var removedEntries []*hostEntry // Remove hosts that we want to ignore. for _, addr := range ignore { node, exists := hdb.activeHosts[addr] if !exists { continue } node.removeNode() delete(hdb.activeHosts, addr) removedEntries = append(removedEntries, node.hostEntry) } // Pick a host, remove it from the tree, and repeat until we have n hosts // or the tree is empty. for len(hosts) < n && !hdb.isEmpty() { randWeight, err := rand.Int(rand.Reader, hdb.hostTree.weight.Big()) if err != nil { break } node, err := hdb.hostTree.nodeAtWeight(types.NewCurrency(randWeight)) if err != nil { break } hosts = append(hosts, node.hostEntry.HostSettings) node.removeNode() delete(hdb.activeHosts, node.hostEntry.NetAddress) removedEntries = append(removedEntries, node.hostEntry) } // Add back all of the entries that got removed. for i := range removedEntries { hdb.insertNode(removedEntries[i]) } return hosts }
// uniformTreeVerification checks that everything makes sense in the tree given // the number of entries that the tree is supposed to have and also given that // every entropy has the same weight. func (hdbt *hdbTester) uniformTreeVerification(numEntries int) error { // Check that the weight of the hostTree is what is expected. expectedWeight := types.NewCurrency64(uint64(numEntries)).Mul(hdbt.hostdb.hostTree.hostEntry.weight) if hdbt.hostdb.hostTree.weight.Cmp(expectedWeight) != 0 { return errors.New("expected weight is incorrect") } // Check that the length of activeHosts and the count of hostTree are // consistent. if len(hdbt.hostdb.activeHosts) != numEntries { return errors.New("unexpected number of active hosts") } // Select many random hosts and do naive statistical analysis on the // results. if !testing.Short() { // Pull a bunch of random hosts and count how many times we pull each // host. selectionMap := make(map[modules.NetAddress]int) expected := 100 for i := 0; i < expected*numEntries; i++ { entries := hdbt.hostdb.RandomHosts(1) if len(entries) == 0 { return errors.New("no hosts!") } selectionMap[entries[0].IPAddress] = selectionMap[entries[0].IPAddress] + 1 } // See if each host was selected enough times. errorBound := 64 // Pretty large, but will still detect if something is seriously wrong. for _, count := range selectionMap { if count < expected-errorBound || count > expected+errorBound { return errors.New("error bound was breached") } } } // Try removing an re-adding all hosts. var removedEntries []*hostEntry for { if hdbt.hostdb.hostTree.weight.IsZero() { break } randWeight, err := rand.Int(rand.Reader, hdbt.hostdb.hostTree.weight.Big()) if err != nil { break } node, err := hdbt.hostdb.hostTree.nodeAtWeight(types.NewCurrency(randWeight)) if err != nil { break } node.removeNode() delete(hdbt.hostdb.activeHosts, node.hostEntry.IPAddress) // remove the entry from the hostdb so it won't be selected as a // repeat. removedEntries = append(removedEntries, node.hostEntry) } for _, entry := range removedEntries { hdbt.hostdb.insertNode(entry) } return nil }
package hostdb import ( "math/big" "github.com/NebulousLabs/Sia/types" ) var ( // Because most weights would otherwise be fractional, we set the base // weight to 10^80 to give ourselves lots of precision when determing the // weight of a host baseWeight = types.NewCurrency(new(big.Int).Exp(big.NewInt(10), big.NewInt(150), nil)) ) // calculateHostWeight returns the weight of a host according to the settings of // the host database entry. Currently, only the price is considered. func calculateHostWeight(entry hostEntry) (weight types.Currency) { // If the price is 0, just return the base weight to avoid divide by zero. price := entry.Price if price.IsZero() { return baseWeight } // Divide the base weight by the price to the fifth power. return baseWeight.Div(price).Div(price).Div(price).Div(price).Div(price) }