From 7647b2594de0cd2a43c2d69a53c8365a6284b08f Mon Sep 17 00:00:00 2001 From: Dave Collins Date: Thu, 22 Feb 2018 20:25:20 -0600 Subject: [PATCH] checkdevpremine: Remove utility. This tool is no longer necessary now that the agreed upon period of time has passed. --- cmd/checkdevpremine/README.md | 75 ------- cmd/checkdevpremine/checkdevpremine.go | 275 ------------------------- cmd/checkdevpremine/config.go | 178 ---------------- 3 files changed, 528 deletions(-) delete mode 100644 cmd/checkdevpremine/README.md delete mode 100644 cmd/checkdevpremine/checkdevpremine.go delete mode 100644 cmd/checkdevpremine/config.go diff --git a/cmd/checkdevpremine/README.md b/cmd/checkdevpremine/README.md deleted file mode 100644 index 5e025404..00000000 --- a/cmd/checkdevpremine/README.md +++ /dev/null @@ -1,75 +0,0 @@ -checkdevpremine -=============== - -The checkdevpremine utility allows transactions to be tested whether or not they -have inputs that trace back to the original dev premine coins. - -It works by using the dcrd RPC server to request all of the relevant input -transactions all the way back to the coinbase output that orignated the coins. -Those coinbase outpoints are then compiled into a list and compared against the -original dev premine outpoints. This is also known as checking for taint. - -The utility only accepts one parameter which can either be a single transaction -hash or a JSON-array of transaction hashes in order to facilite checking -multiple at once. That parameter may also be a single dash, `-`, in order to -indicate it should be read from stdin. - -Any outpoints which are found to be part of the original dev premine will be -listed unless the `--quiet` flag is provided to suppress the output. - -In addition, in order to facilitate programmatic access the tool returns the -following codes to the Operating System: - -|Return Code|Description| -|---|---| -|0|The transaction(s) do _NOT_ have any inputs which trace back to the dev premine coins| -|1|One of more of the transactions _DO_ have at least one input which traces back to the dev premine coins| -|2|Some type of error such as inability to talk to the RPC server occurred| - -These codes in addition with the `--quiet` flag allow a fully automated check -with no visible output. - -## Configuring - -In order to connect and authenticate to the dcrd RPC server, the `--rpcuser` and -`--rpcpass` options must be specified. These can be placed into a config file -named `checkdevpremine.conf` at the location shown in the help output of the -utility (`checkdevpremine -h`). - -Config file example: - -``` -rpcuser=your_dcrd_RPC_server_username -rpcpass=your_dcrd_RPC_server_password -``` - -## Example Usage - -Checking a single transaction with visible output: -```bash -$ checkdevpremine 25afd7d33ceb8698f5d81eb7ee14a7532419ce1e1c65cc5032e37696f26c5cac -``` - -Checking for multiple transactions with visible output: -```bash -$ checkdevpremine "[\"25afd7d33ceb8698f5d81eb7ee14a7532419ce1e1c65cc5032e37696f26c5cac\", \"b1527b63c7a76ea28e57604082bec0e8195cd7a33bd10c68296a45bce77bd2db\"]" -``` - -Mixing tools in order to check all of the transactions in the latest block using -[jq](https://stedolan.github.io/jq/) to extract the transaction hashes and feed -them to `checkdevpremine` via stdin along with `--quiet` to suppress output: -```bash -$ dcrctl getbestblockhash | dcrctl getblock - | jq -c .tx | checkdevpremine --quiet -; echo $? -0 -``` - -## Installation and Updating - -```bash -$ go get -u github.com/decred/dcrd/cmd/checkdevpremine -``` - -## License - -The checkdevpremine utility is licensed under the [copyfree](http://copyfree.org) -ISC License. diff --git a/cmd/checkdevpremine/checkdevpremine.go b/cmd/checkdevpremine/checkdevpremine.go deleted file mode 100644 index 2317c278..00000000 --- a/cmd/checkdevpremine/checkdevpremine.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright (c) 2016 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package main - -import ( - "bufio" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/decred/dcrd/blockchain" - "github.com/decred/dcrd/blockchain/stake" - "github.com/decred/dcrd/chaincfg/chainhash" - "github.com/decred/dcrd/rpcclient" - "github.com/decred/dcrd/wire" -) - -// Codes that are returned to the operating system. -const ( - rcNoDevPremineInputs = 0 - rcDevPremineInputs = 1 - rcError = 2 -) - -const ( - // devCoinMaxIndex is the final index in the block 1 premine transaction - // that involves the original developer premine coins. All coins after - // this index are part of the airdrop. - devCoinMaxIndex = 173 -) - -var ( - // premineTxHash is the hash of the transaction in block one which - // creates the premine coins. - premineTxHash = newHashFromStr("5e29cdb355b3fc7e76c98a9983cd44324b3efdd7815c866e33f6c72292cb8be6") -) - -// newHashFromStr converts the passed big-endian hex string into a -// chainhash.Hash. It only differs from the one available in chainhash in that -// it panics on an error since it will only (and must only) be called with -// hard-coded, and therefore known good, hashes. -func newHashFromStr(hexStr string) *chainhash.Hash { - hash, err := chainhash.NewHashFromStr(hexStr) - if err != nil { - panic(err) - } - return hash -} - -// usage displays the general usage when the help flag is not displayed and -// and no single argument was specified. -func usage(errorMessage string) { - appName := filepath.Base(os.Args[0]) - appName = strings.TrimSuffix(appName, filepath.Ext(appName)) - fmt.Fprintln(os.Stderr, errorMessage) - fmt.Fprintln(os.Stderr, "Usage:") - fmt.Fprintf(os.Stderr, " %s [OPTIONS] \n\n", - appName) - fmt.Fprintln(os.Stderr, "Specify -h to show available options") -} - -// isDevPremineOut return whether or not the provided outpoint is one of the -// original dev premine coins. -func isDevPremineOut(out wire.OutPoint) bool { - return out.Hash.IsEqual(premineTxHash) && out.Index <= devCoinMaxIndex && - out.Tree == 0 -} - -// traceDevPremineOuts returns a list of outpoints that are part of the dev -// premine coins and are ancestors of the inputs to the passed transaction hash. -func traceDevPremineOuts(client *rpcclient.Client, txHash *chainhash.Hash) ([]wire.OutPoint, error) { - // Trace the lineage of all inputs to the provided transaction back to - // the coinbase outputs that generated them and add those outpoints to - // a list. Also, keep track of all of the processed transactions in - // order to avoid processing duplicates. - knownCoinbases := make(map[chainhash.Hash]struct{}) - processedHashes := make(map[chainhash.Hash]struct{}) - coinbaseOuts := make([]wire.OutPoint, 0, 10) - processOuts := []wire.OutPoint{{Hash: *txHash}} - for len(processOuts) > 0 { - // Grab the first outpoint to process and skip it if it has - // already been traced. - outpoint := processOuts[0] - processOuts = processOuts[1:] - if _, exists := processedHashes[outpoint.Hash]; exists { - if _, exists := knownCoinbases[outpoint.Hash]; exists { - coinbaseOuts = append(coinbaseOuts, outpoint) - } - continue - } - processedHashes[outpoint.Hash] = struct{}{} - - // Request the transaction for the outpoint from the server. - tx, err := client.GetRawTransaction(&outpoint.Hash) - if err != nil { - return nil, fmt.Errorf("failed to get transaction %v: %v", - &outpoint.Hash, err) - } - - // Add the outpoint to the coinbase outputs list when it is part - // of a coinbase transaction. Also, keep track of the fact the - // transaction is a coinbase to use when avoiding duplicate - // checks. - if blockchain.IsCoinBase(tx) { - knownCoinbases[outpoint.Hash] = struct{}{} - coinbaseOuts = append(coinbaseOuts, outpoint) - continue - } - - // Add the inputs to the transaction to the list of transactions - // to load and continue tracing. - // - // However, skip the first input to stake generation txns since - // they are creating new coins. The remaining inputs to a - // stake generation transaction still need to be traced since - // they represent the coins that purchased the ticket. - txIns := tx.MsgTx().TxIn - if stake.IsSSGen(tx.MsgTx()) { - txIns = txIns[1:] - } - for _, txIn := range txIns { - processOuts = append(processOuts, txIn.PreviousOutPoint) - } - } - - // Add any of the outputs that are dev premine outputs to a list. - var devPremineOuts []wire.OutPoint - for _, coinbaseOut := range coinbaseOuts { - if isDevPremineOut(coinbaseOut) { - devPremineOuts = append(devPremineOuts, coinbaseOut) - } - } - - return devPremineOuts, nil -} - -// realMain is the real main function for the utility. It is necessary to work -// around the fact that deferred functions do not run when os.Exit() is called. -func realMain() int { - // Load configuration and parse command line. - cfg, args, err := loadConfig() - if err != nil { - return rcError - } - - // Ensure the user specified a single argument. - if len(args) < 1 { - usage("Transaction hash not specified") - return rcError - } - if len(args) > 1 { - usage("Too many arguments specified") - return rcError - } - - // Read the argument from a stdin pipe when it is '-'. - arg0 := args[0] - if arg0 == "-" { - bio := bufio.NewReader(os.Stdin) - param, err := bio.ReadString('\n') - if err != nil && err != io.EOF { - fmt.Fprintf(os.Stderr, "Failed to read data from "+ - "stdin: %v\n", err) - return rcError - } - if err == io.EOF && len(param) == 0 { - fmt.Fprintln(os.Stderr, "Not enough lines provided on "+ - "stdin") - return rcError - } - arg0 = param - } - arg0 = strings.TrimSpace(arg0) - - // Attempt to unmarshal the parameter as a JSON array of strings if it - // looks like JSON input. This allows multiple transactions to be - // specified via the argument. Treat the argument as a single hash if - // it fails to unmarshal. - var txHashes []*chainhash.Hash - if strings.Contains(arg0, "[") && strings.Contains(arg0, "]") { - var txHashStrs []string - if err := json.Unmarshal([]byte(arg0), &txHashStrs); err != nil { - fmt.Fprintf(os.Stderr, "Failed to unmarshal JSON "+ - "string array of transaction hashes: %v\n", err) - return rcError - } - for _, txHashStr := range txHashStrs { - txHash, err := chainhash.NewHashFromStr(txHashStr) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to parse "+ - "transaction hash %q: %v\n", txHashStr, err) - return rcError - } - txHashes = append(txHashes, txHash) - } - } else { - // Parse the provided transaction hash string. - arg0 = strings.Trim(arg0, `"`) - txHash, err := chainhash.NewHashFromStr(arg0) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to parse transaction "+ - "hash %q: %v\n", arg0, err) - return rcError - } - txHashes = append(txHashes, txHash) - } - - // Connect to dcrd RPC server using websockets. - certs, err := ioutil.ReadFile(cfg.RPCCert) - if err != nil { - fmt.Fprintf(os.Stderr, "Failed to read RPC server TLS cert: %v\n", - err) - return rcError - } - connCfg := &rpcclient.ConnConfig{ - Host: cfg.RPCServer, - Endpoint: "ws", - User: cfg.RPCUser, - Pass: cfg.RPCPassword, - DisableTLS: cfg.NoTLS, - Certificates: certs, - } - client, err := rpcclient.New(connCfg, nil) - if err != nil { - fmt.Fprintf(os.Stderr, "Unable to connect to dcrd RPC server: "+ - "%v\n", err) - return rcError - } - defer client.Shutdown() - - // Check all of the provided transactions. - var hasDevPremineOuts bool - for _, txHash := range txHashes { - // Get a list of all dev premine outpoints the are ancestors of - // all inputs to the provided transaction. - devPremineOuts, err := traceDevPremineOuts(client, txHash) - if err != nil { - fmt.Fprintln(os.Stderr, err) - return rcError - } - - // List outputs which are dev premine outputs. - if len(devPremineOuts) > 0 { - hasDevPremineOuts = true - - // Don't print anything in quiet mode. - if cfg.Quiet { - continue - } - fmt.Printf("Transaction %v contains inputs which "+ - "trace back to the following original dev "+ - "premine outpoints:\n", txHash) - for _, out := range devPremineOuts { - fmt.Println(out) - } - } - } - - // Return the approriate code depending on whether or not any of the - // inputs trace back to a dev premine outpoint. - if hasDevPremineOuts { - return rcDevPremineInputs - } - return rcNoDevPremineInputs -} - -func main() { - os.Exit(realMain()) -} diff --git a/cmd/checkdevpremine/config.go b/cmd/checkdevpremine/config.go deleted file mode 100644 index 37edddad..00000000 --- a/cmd/checkdevpremine/config.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2016 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package main - -import ( - "fmt" - "net" - "os" - "os/user" - "path/filepath" - "runtime" - "strings" - - "github.com/decred/dcrd/dcrutil" - - flags "github.com/jessevdk/go-flags" -) - -var ( - dcrdHomeDir = dcrutil.AppDataDir("dcrd", false) - appHomeDir = dcrutil.AppDataDir("checkdevpremine", false) - defaultConfigFile = filepath.Join(appHomeDir, "checkdevpremine.conf") - defaultRPCServer = "localhost" - defaultRPCCertFile = filepath.Join(dcrdHomeDir, "rpc.cert") -) - -// config defines the configuration options for dcrctl. -// -// See loadConfig for details on the configuration load process. -type config struct { - ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` - RPCUser string `short:"u" long:"rpcuser" description:"RPC username"` - RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"` - RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"` - RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"` - NoTLS bool `long:"notls" description:"Disable TLS"` - Quiet bool `long:"quiet" description:"Do not print any found outpoints which can be useful if only relying on the return code"` -} - -// normalizeAddress returns addr with the default port appended if there is not -// already a port specified. -func normalizeAddress(addr string) string { - _, _, err := net.SplitHostPort(addr) - if err != nil { - return net.JoinHostPort(addr, "9109") - } - return addr -} - -// cleanAndExpandPath expands environment variables and leading ~ in the -// passed path, cleans the result, and returns it. -func cleanAndExpandPath(path string) string { - // NOTE: The os.ExpandEnv doesn't work with Windows cmd.exe-style - // %VARIABLE%, but the variables can still be expanded via POSIX-style - // $VARIABLE. - path = os.ExpandEnv(path) - - if !strings.HasPrefix(path, "~") { - return filepath.Clean(path) - } - - // Expand initial ~ to the current user's home directory, or ~otheruser - // to otheruser's home directory. On Windows, both forward and backward - // slashes can be used. - path = path[1:] - - var pathSeparators string - if runtime.GOOS == "windows" { - pathSeparators = string(os.PathSeparator) + "/" - } else { - pathSeparators = string(os.PathSeparator) - } - - userName := "" - if i := strings.IndexAny(path, pathSeparators); i != -1 { - userName = path[:i] - path = path[i:] - } - - homeDir := "" - var u *user.User - var err error - if userName == "" { - u, err = user.Current() - } else { - u, err = user.Lookup(userName) - } - if err == nil { - homeDir = u.HomeDir - } - // Fallback to CWD if user lookup fails or user has no home directory. - if homeDir == "" { - homeDir = "." - } - - return filepath.Join(homeDir, path) -} - -// loadConfig initializes and parses the config using a config file and command -// line options. -// -// The configuration proceeds as follows: -// 1) Start with a default config with sane settings -// 2) Pre-parse the command line to check for an alternative config file -// 3) Load configuration file overwriting defaults with any specified options -// 4) Parse CLI options and overwrite/add any specified options -// -// The above results in functioning properly without any config settings -// while still allowing the user to override settings with config files and -// command line options. Command line options always take precedence. -func loadConfig() (*config, []string, error) { - // Default config. - cfg := config{ - ConfigFile: defaultConfigFile, - RPCServer: defaultRPCServer, - RPCCert: defaultRPCCertFile, - } - - // Create the home directory if it doesn't already exist. - err := os.MkdirAll(dcrdHomeDir, 0700) - if err != nil { - fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(-1) - } - - // Pre-parse the command line options to see if an alternative config - // file, the version flag, or the list commands flag was specified. Any - // errors aside from the help message error can be ignored here since - // they will be caught by the final parse below. - preCfg := cfg - preParser := flags.NewParser(&preCfg, flags.HelpFlag) - _, err = preParser.Parse() - if err != nil { - if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { - fmt.Fprintln(os.Stderr, err) - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, "The special parameter `-` "+ - "indicates that a parameter should be read "+ - "from the\nnext unread line from standard "+ - "input.") - return nil, nil, err - } - } - - // Load additional config from file. - appName := filepath.Base(os.Args[0]) - appName = strings.TrimSuffix(appName, filepath.Ext(appName)) - usageMessage := fmt.Sprintf("Use %s -h to show options", appName) - parser := flags.NewParser(&cfg, flags.Default) - err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile) - if err != nil { - if _, ok := err.(*os.PathError); !ok { - fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n", - err) - fmt.Fprintln(os.Stderr, usageMessage) - return nil, nil, err - } - } - - // Parse command line options again to ensure they take precedence. - remainingArgs, err := parser.Parse() - if err != nil { - if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp { - fmt.Fprintln(os.Stderr, usageMessage) - } - return nil, nil, err - } - - // Handle environment variable expansion in the RPC certificate path. - cfg.RPCCert = cleanAndExpandPath(cfg.RPCCert) - - // Add default port to RPC server if needed. - cfg.RPCServer = normalizeAddress(cfg.RPCServer) - - return &cfg, remainingArgs, nil -}