github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/cmd/spc/parse.go (about) 1 package main 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "math" 10 "math/big" 11 "os" 12 "strings" 13 14 "SiaPrime/encoding" 15 "SiaPrime/types" 16 ) 17 18 var errUnableToParseSize = errors.New("unable to parse size") 19 20 // filesize returns a string that displays a filesize in human-readable units. 21 func filesizeUnits(size int64) string { 22 if size == 0 { 23 return "0 B" 24 } 25 sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} 26 i := int(math.Log10(float64(size)) / 3) 27 return fmt.Sprintf("%.*f %s", i, float64(size)/math.Pow10(3*i), sizes[i]) 28 } 29 30 // parseFilesize converts strings of form 10GB to a size in bytes. Fractional 31 // sizes are truncated at the byte size. 32 func parseFilesize(strSize string) (string, error) { 33 units := []struct { 34 suffix string 35 multiplier int64 36 }{ 37 {"kb", 1e3}, 38 {"mb", 1e6}, 39 {"gb", 1e9}, 40 {"tb", 1e12}, 41 {"kib", 1 << 10}, 42 {"mib", 1 << 20}, 43 {"gib", 1 << 30}, 44 {"tib", 1 << 40}, 45 {"b", 1}, // must be after others else it'll match on them all 46 } 47 48 strSize = strings.ToLower(strSize) 49 for _, unit := range units { 50 if strings.HasSuffix(strSize, unit.suffix) { 51 r, ok := new(big.Rat).SetString(strings.TrimSuffix(strSize, unit.suffix)) 52 if !ok { 53 return "", errUnableToParseSize 54 } 55 r.Mul(r, new(big.Rat).SetInt(big.NewInt(unit.multiplier))) 56 if !r.IsInt() { 57 f, _ := r.Float64() 58 return fmt.Sprintf("%d", int64(f)), nil 59 } 60 return r.RatString(), nil 61 } 62 } 63 64 return "", errUnableToParseSize 65 } 66 67 // periodUnits turns a period in terms of blocks to a number of weeks. 68 func periodUnits(blocks types.BlockHeight) string { 69 return fmt.Sprint(blocks / 1008) // 1008 blocks per week 70 } 71 72 // parsePeriod converts a duration specified in blocks, hours, or weeks to a 73 // number of blocks. 74 func parsePeriod(period string) (string, error) { 75 units := []struct { 76 suffix string 77 multiplier float64 78 }{ 79 {"b", 1}, // blocks 80 {"block", 1}, // blocks 81 {"blocks", 1}, // blocks 82 {"h", 6}, // hours 83 {"hour", 6}, // hours 84 {"hours", 6}, // hours 85 {"d", 144}, // days 86 {"day", 144}, // days 87 {"days", 144}, // days 88 {"w", 1008}, // weeks 89 {"week", 1008}, // weeks 90 {"weeks", 1008}, // weeks 91 } 92 93 period = strings.ToLower(period) 94 for _, unit := range units { 95 if strings.HasSuffix(period, unit.suffix) { 96 var base float64 97 _, err := fmt.Sscan(strings.TrimSuffix(period, unit.suffix), &base) 98 if err != nil { 99 return "", errUnableToParseSize 100 } 101 blocks := int(base * unit.multiplier) 102 return fmt.Sprint(blocks), nil 103 } 104 } 105 106 return "", errUnableToParseSize 107 } 108 109 // currencyUnits converts a types.Currency to a string with human-readable 110 // units. The unit used will be the largest unit that results in a value 111 // greater than 1. The value is rounded to 4 significant digits. 112 func currencyUnits(c types.Currency) string { 113 pico := types.SiacoinPrecision.Div64(1e12) 114 if c.Cmp(pico) < 0 { 115 return c.String() + " H" 116 } 117 118 // iterate until we find a unit greater than c 119 mag := pico 120 unit := "" 121 for _, unit = range []string{"pS", "nS", "uS", "mS", "SCP", "KS", "MS", "GS", "TS"} { 122 if c.Cmp(mag.Mul64(1e3)) < 0 { 123 break 124 } else if unit != "TS" { 125 // don't want to perform this multiply on the last iter; that 126 // would give us 1.235 TS instead of 1235 TS 127 mag = mag.Mul64(1e3) 128 } 129 } 130 131 num := new(big.Rat).SetInt(c.Big()) 132 denom := new(big.Rat).SetInt(mag.Big()) 133 res, _ := new(big.Rat).Mul(num, denom.Inv(denom)).Float64() 134 135 return fmt.Sprintf("%.4g %s", res, unit) 136 } 137 138 // parseCurrency converts a siacoin amount to base units. 139 func parseCurrency(amount string) (string, error) { 140 units := []string{"pS", "nS", "uS", "mS", "SCP", "KS", "MS", "GS", "TS"} 141 for i, unit := range units { 142 if strings.HasSuffix(amount, unit) { 143 // scan into big.Rat 144 r, ok := new(big.Rat).SetString(strings.TrimSuffix(amount, unit)) 145 if !ok { 146 return "", errors.New("malformed amount") 147 } 148 // convert units 149 exp := 24 + 3*(int64(i)-4) 150 mag := new(big.Int).Exp(big.NewInt(10), big.NewInt(exp), nil) 151 r.Mul(r, new(big.Rat).SetInt(mag)) 152 // r must be an integer at this point 153 if !r.IsInt() { 154 return "", errors.New("non-integer number of hastings") 155 } 156 return r.RatString(), nil 157 } 158 } 159 // check for hastings separately 160 if strings.HasSuffix(amount, "H") { 161 return strings.TrimSuffix(amount, "H"), nil 162 } 163 164 return "", errors.New("amount is missing units; run 'wallet --help' for a list of units") 165 } 166 167 // yesNo returns "Yes" if b is true, and "No" if b is false. 168 func yesNo(b bool) string { 169 if b { 170 return "Yes" 171 } 172 return "No" 173 } 174 175 // parseTxn decodes a transaction from s, which can be JSON, base64, or a path 176 // to a file containing either encoding. 177 func parseTxn(s string) (types.Transaction, error) { 178 // first assume s is a file 179 txnBytes, err := ioutil.ReadFile(s) 180 if os.IsNotExist(err) { 181 // assume s is a literal encoding 182 txnBytes = []byte(s) 183 } else if err != nil { 184 return types.Transaction{}, errors.New("could not read transaction file: " + err.Error()) 185 } 186 // txnBytes now contains either s or the contents of the file, so it is 187 // either JSON or base64 188 var txn types.Transaction 189 if json.Valid(txnBytes) { 190 if err := json.Unmarshal(txnBytes, &txn); err != nil { 191 return types.Transaction{}, errors.New("could not decode JSON transaction: " + err.Error()) 192 } 193 } else { 194 bin, err := base64.StdEncoding.DecodeString(string(txnBytes)) 195 if err != nil { 196 return types.Transaction{}, errors.New("argument is not valid JSON, base64, or filepath") 197 } 198 if err := encoding.Unmarshal(bin, &txn); err != nil { 199 return types.Transaction{}, errors.New("could not decode binary transaction: " + err.Error()) 200 } 201 } 202 return txn, nil 203 }