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  }