github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/cmd/spc/utils.go (about) 1 package main 2 3 import ( 4 "encoding/base64" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "log" 9 "os" 10 "strconv" 11 "strings" 12 13 "github.com/spf13/cobra" 14 "github.com/spf13/cobra/doc" 15 "SiaPrime/crypto" 16 "SiaPrime/encoding" 17 "SiaPrime/types" 18 ) 19 20 var ( 21 utilsCmd = &cobra.Command{ 22 Use: "utils", 23 Short: "various utilities for working with SiaPrimes's types", 24 Long: `Various utilities for working with SiaPrimes's types. 25 These commands do not require spd.`, 26 // Run field not provided; utils requires a subcommand. 27 } 28 29 bashcomplCmd = &cobra.Command{ 30 Use: "bash-completion [path]", 31 Short: "Creates bash completion file.", 32 Long: `Creates a bash completion file at the specified location. 33 34 Note: Bash completions will only work with the prefix with which the script 35 is created (e.g. ./spc or spc). 36 37 Once created, the file has to be moved to the bash completion script folder, 38 usually /etc/bash_completion.d/`, 39 Run: wrap(bashcomplcmd), 40 } 41 42 mangenCmd = &cobra.Command{ 43 Use: "man-generation [path]", 44 Short: "Creates unix style manpages.", 45 Long: "Creates unix style man pages at the specified directory.", 46 Run: wrap(mangencmd), 47 } 48 49 utilsHastingsCmd = &cobra.Command{ 50 Use: "hastings [amount]", 51 Short: "convert a currency amount to Hastings", 52 Long: `Convert a currency amount to Hastings. 53 See wallet --help for a list of units.`, 54 Run: wrap(utilshastingscmd), 55 } 56 57 utilsEncodeRawTxnCmd = &cobra.Command{ 58 Use: "encoderawtxn [json txn]", 59 Short: "convert a JSON-encoded transaction to base64", 60 Long: `Convert a JSON-encoded transaction to base64. 61 The argument may be either a JSON literal or a file containing JSON.`, 62 Run: wrap(utilsencoderawtxncmd), 63 } 64 65 utilsDecodeRawTxnCmd = &cobra.Command{ 66 Use: "decoderawtxn [base64 txn]", 67 Short: "convert a base64-encoded transaction to JSON", 68 Long: `Convert a base64-encoded transaction to JSON.`, 69 Run: wrap(utilsdecoderawtxncmd), 70 } 71 72 utilsSigHashCmd = &cobra.Command{ 73 Use: "sighash [sig index] [txn]", 74 Short: "calculate the SigHash of a transaction", 75 Long: `Calculate the SigHash of a transaction. 76 The SigHash is the hash of the fields of the transaction specified 77 in the CoveredFields of the specified signature. 78 The transaction may be JSON, base64, or a file containing either.`, 79 Run: wrap(utilssighashcmd), 80 } 81 82 utilsCheckSigCmd = &cobra.Command{ 83 Use: "checksig [sig] [hash] [pubkey]", 84 Short: "verify a signature of the specified hash", 85 Long: `Verify that a hash was signed by the specified key. 86 87 The signature should be base64-encoded, and the hash should be hex-encoded. 88 The pubkey should be either a JSON-encoded SiaPublicKey, or of the form: 89 algorithm:hexkey 90 e.g. ed25519:d0e1a2d3b4e5e6f7... 91 92 Use sighash to calculate the hash of a transaction. 93 `, 94 Run: wrap(utilschecksigcmd), 95 } 96 ) 97 98 func bashcomplcmd(path string) { 99 rootCmd.GenBashCompletionFile(path) 100 } 101 102 func mangencmd(path string) { 103 doc.GenManTree(rootCmd, &doc.GenManHeader{ 104 Section: "1", 105 Manual: "spc Manual", 106 Source: "", 107 }, path) 108 } 109 110 func utilshastingscmd(amount string) { 111 hastings, err := parseCurrency(amount) 112 if err != nil { 113 die(err) 114 } 115 fmt.Println(hastings) 116 } 117 118 func utilsdecoderawtxncmd(b64 string) { 119 bin, err := base64.StdEncoding.DecodeString(b64) 120 if err != nil { 121 die("Invalid base64:", err) 122 } 123 var txn types.Transaction 124 if err := encoding.Unmarshal(bin, &txn); err != nil { 125 die("Invalid transaction:", err) 126 } 127 js, _ := json.MarshalIndent(txn, "", "\t") 128 fmt.Println(string(js)) 129 } 130 131 func utilsencoderawtxncmd(jstxn string) { 132 var jsBytes []byte 133 if strings.HasPrefix(strings.TrimSpace(jstxn), "{") { 134 // assume JSON if arg starts with { 135 jsBytes = []byte(jstxn) 136 } else { 137 // otherwise, assume it's a file containing JSON 138 var err error 139 jsBytes, err = ioutil.ReadFile(jstxn) 140 if err != nil { 141 die("Could not read JSON file:", err) 142 } 143 } 144 var txn types.Transaction 145 if err := json.Unmarshal(jsBytes, &txn); err != nil { 146 die("Invalid transaction:", err) 147 } 148 fmt.Println(base64.StdEncoding.EncodeToString(encoding.Marshal(txn))) 149 } 150 151 func utilssighashcmd(indexStr, txnStr string) { 152 index, err := strconv.Atoi(indexStr) 153 if err != nil { 154 die("Sig index must be an integer") 155 } 156 157 // assume txn is a file 158 txnBytes, err := ioutil.ReadFile(txnStr) 159 if os.IsNotExist(err) { 160 // assume txn is a literal encoding 161 txnBytes = []byte(txnStr) 162 } else if err != nil { 163 die("Could not read JSON file:", err) 164 } 165 // txnBytes is either JSON or base64 166 var txn types.Transaction 167 if json.Valid(txnBytes) { 168 if err := json.Unmarshal(txnBytes, &txn); err != nil { 169 die("Could not decode JSON:", err) 170 } 171 } else { 172 bin, err := base64.StdEncoding.DecodeString(string(txnBytes)) 173 if err != nil { 174 die("Could not decode txn as JSON, base64, or file") 175 } 176 if err := encoding.Unmarshal(bin, &txn); err != nil { 177 die("Could not decode binary transaction:", err) 178 } 179 } 180 181 fmt.Println(txn.SigHash(index, 180e3)) 182 } 183 184 func utilschecksigcmd(base64Sig, hexHash, pkStr string) { 185 var sig crypto.Signature 186 sigBytes, err := base64.StdEncoding.DecodeString(base64Sig) 187 if err != nil || copy(sig[:], sigBytes) != len(sig) { 188 die("Couldn't parse signature") 189 } 190 var hash crypto.Hash 191 if err := hash.LoadString(hexHash); err != nil { 192 die("Couldn't parse hash") 193 } 194 var spk types.SiaPublicKey 195 if spk.LoadString(pkStr); len(spk.Key) == 0 { 196 if err := json.Unmarshal([]byte(pkStr), &spk); err != nil { 197 die("Couldn't parse pubkey") 198 } 199 } 200 if spk.Algorithm != types.SignatureEd25519 { 201 die("Only ed25519 signatures are supported") 202 } 203 var pk crypto.PublicKey 204 copy(pk[:], spk.Key) 205 206 if crypto.VerifyHash(hash, pk, sig) == nil { 207 fmt.Println("Verified OK") 208 } else { 209 log.Fatalln("Bad signature") 210 } 211 }