github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/cmd/clef/main.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"bufio"
    21  	"context"
    22  	"crypto/rand"
    23  	"crypto/sha256"
    24  	"encoding/hex"
    25  	"encoding/json"
    26  	"fmt"
    27  	"io"
    28  	"io/ioutil"
    29  	"math/big"
    30  	"os"
    31  	"os/signal"
    32  	"path/filepath"
    33  	"runtime"
    34  	"sort"
    35  	"strings"
    36  	"syscall"
    37  	"time"
    38  
    39  	"github.com/kisexp/xdchain/accounts"
    40  	"github.com/kisexp/xdchain/accounts/keystore"
    41  	"github.com/kisexp/xdchain/accounts/pluggable"
    42  	"github.com/kisexp/xdchain/cmd/utils"
    43  	"github.com/kisexp/xdchain/common"
    44  	"github.com/kisexp/xdchain/common/hexutil"
    45  	"github.com/kisexp/xdchain/core/types"
    46  	"github.com/kisexp/xdchain/crypto"
    47  	"github.com/kisexp/xdchain/internal/debug"
    48  	"github.com/kisexp/xdchain/internal/ethapi"
    49  	"github.com/kisexp/xdchain/internal/flags"
    50  	"github.com/kisexp/xdchain/log"
    51  	"github.com/kisexp/xdchain/node"
    52  	"github.com/kisexp/xdchain/params"
    53  	"github.com/kisexp/xdchain/plugin"
    54  	"github.com/kisexp/xdchain/plugin/account"
    55  	"github.com/kisexp/xdchain/rlp"
    56  	"github.com/kisexp/xdchain/rpc"
    57  	"github.com/kisexp/xdchain/signer/core"
    58  	"github.com/kisexp/xdchain/signer/fourbyte"
    59  	"github.com/kisexp/xdchain/signer/rules"
    60  	"github.com/kisexp/xdchain/signer/storage"
    61  
    62  	"github.com/mattn/go-colorable"
    63  	"github.com/mattn/go-isatty"
    64  	"gopkg.in/urfave/cli.v1"
    65  )
    66  
    67  const legalWarning = `
    68  WARNING!
    69  
    70  Clef is an account management tool. It may, like any software, contain bugs.
    71  
    72  Please take care to
    73  - backup your keystore files,
    74  - verify that the keystore(s) can be opened with your password.
    75  
    76  Clef is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    77  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
    78  PURPOSE. See the GNU General Public License for more details.
    79  `
    80  
    81  var (
    82  	logLevelFlag = cli.IntFlag{
    83  		Name:  "loglevel",
    84  		Value: 4,
    85  		Usage: "log level to emit to the screen",
    86  	}
    87  	advancedMode = cli.BoolFlag{
    88  		Name:  "advanced",
    89  		Usage: "If enabled, issues warnings instead of rejections for suspicious requests. Default off",
    90  	}
    91  	acceptFlag = cli.BoolFlag{
    92  		Name:  "suppress-bootwarn",
    93  		Usage: "If set, does not show the warning during boot",
    94  	}
    95  	keystoreFlag = cli.StringFlag{
    96  		Name:  "keystore",
    97  		Value: filepath.Join(node.DefaultDataDir(), "keystore"),
    98  		Usage: "Directory for the keystore",
    99  	}
   100  	configdirFlag = cli.StringFlag{
   101  		Name:  "configdir",
   102  		Value: DefaultConfigDir(),
   103  		Usage: "Directory for Clef configuration",
   104  	}
   105  	chainIdFlag = cli.Int64Flag{
   106  		Name:  "chainid",
   107  		Value: params.MainnetChainConfig.ChainID.Int64(),
   108  		Usage: "Chain id to use for signing (1=mainnet, 3=Ropsten, 4=Rinkeby, 5=Goerli)",
   109  	}
   110  	rpcPortFlag = cli.IntFlag{
   111  		Name:  "http.port",
   112  		Usage: "HTTP-RPC server listening port",
   113  		Value: node.DefaultHTTPPort + 5,
   114  	}
   115  	legacyRPCPortFlag = cli.IntFlag{
   116  		Name:  "rpcport",
   117  		Usage: "HTTP-RPC server listening port (Deprecated, please use --http.port).",
   118  		Value: node.DefaultHTTPPort + 5,
   119  	}
   120  	signerSecretFlag = cli.StringFlag{
   121  		Name:  "signersecret",
   122  		Usage: "A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash",
   123  	}
   124  	customDBFlag = cli.StringFlag{
   125  		Name:  "4bytedb-custom",
   126  		Usage: "File used for writing new 4byte-identifiers submitted via API",
   127  		Value: "./4byte-custom.json",
   128  	}
   129  	auditLogFlag = cli.StringFlag{
   130  		Name:  "auditlog",
   131  		Usage: "File used to emit audit logs. Set to \"\" to disable",
   132  		Value: "audit.log",
   133  	}
   134  	ruleFlag = cli.StringFlag{
   135  		Name:  "rules",
   136  		Usage: "Path to the rule file to auto-authorize requests with",
   137  	}
   138  	stdiouiFlag = cli.BoolFlag{
   139  		Name: "stdio-ui",
   140  		Usage: "Use STDIN/STDOUT as a channel for an external UI. " +
   141  			"This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user " +
   142  			"interface, and can be used when Clef is started by an external process.",
   143  	}
   144  	testFlag = cli.BoolFlag{
   145  		Name:  "stdio-ui-test",
   146  		Usage: "Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.",
   147  	}
   148  	app         = cli.NewApp()
   149  	initCommand = cli.Command{
   150  		Action:    utils.MigrateFlags(initializeSecrets),
   151  		Name:      "init",
   152  		Usage:     "Initialize the signer, generate secret storage",
   153  		ArgsUsage: "",
   154  		Flags: []cli.Flag{
   155  			logLevelFlag,
   156  			configdirFlag,
   157  		},
   158  		Description: `
   159  The init command generates a master seed which Clef can use to store credentials and data needed for
   160  the rule-engine to work.`,
   161  	}
   162  	attestCommand = cli.Command{
   163  		Action:    utils.MigrateFlags(attestFile),
   164  		Name:      "attest",
   165  		Usage:     "Attest that a js-file is to be used",
   166  		ArgsUsage: "<sha256sum>",
   167  		Flags: []cli.Flag{
   168  			logLevelFlag,
   169  			configdirFlag,
   170  			signerSecretFlag,
   171  		},
   172  		Description: `
   173  The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of
   174  incoming requests.
   175  
   176  Whenever you make an edit to the rule file, you need to use attestation to tell
   177  Clef that the file is 'safe' to execute.`,
   178  	}
   179  	setCredentialCommand = cli.Command{
   180  		Action:    utils.MigrateFlags(setCredential),
   181  		Name:      "setpw",
   182  		Usage:     "Store a credential for a keystore file",
   183  		ArgsUsage: "<address>",
   184  		Flags: []cli.Flag{
   185  			logLevelFlag,
   186  			configdirFlag,
   187  			signerSecretFlag,
   188  		},
   189  		Description: `
   190  The setpw command stores a password for a given address (keyfile).
   191  `}
   192  	delCredentialCommand = cli.Command{
   193  		Action:    utils.MigrateFlags(removeCredential),
   194  		Name:      "delpw",
   195  		Usage:     "Remove a credential for a keystore file",
   196  		ArgsUsage: "<address>",
   197  		Flags: []cli.Flag{
   198  			logLevelFlag,
   199  			configdirFlag,
   200  			signerSecretFlag,
   201  		},
   202  		Description: `
   203  The delpw command removes a password for a given address (keyfile).
   204  `}
   205  	newAccountCommand = cli.Command{
   206  		Action:    utils.MigrateFlags(newAccount),
   207  		Name:      "newaccount",
   208  		Usage:     "Create a new account",
   209  		ArgsUsage: "",
   210  		Flags: []cli.Flag{
   211  			logLevelFlag,
   212  			keystoreFlag,
   213  			utils.LightKDFFlag,
   214  			acceptFlag,
   215  		},
   216  		Description: `
   217  The newaccount command creates a new keystore-backed account. It is a convenience-method
   218  which can be used in lieu of an external UI.`,
   219  	}
   220  
   221  	gendocCommand = cli.Command{
   222  		Action: GenDoc,
   223  		Name:   "gendoc",
   224  		Usage:  "Generate documentation about json-rpc format",
   225  		Description: `
   226  The gendoc generates example structures of the json-rpc communication types.
   227  `}
   228  )
   229  
   230  // <Quorum>
   231  var supportedPlugins = []plugin.PluginInterfaceName{plugin.AccountPluginInterfaceName}
   232  
   233  // </Quorum>
   234  
   235  // AppHelpFlagGroups is the application flags, grouped by functionality.
   236  var AppHelpFlagGroups = []flags.FlagGroup{
   237  	{
   238  		Name: "FLAGS",
   239  		Flags: []cli.Flag{
   240  			logLevelFlag,
   241  			keystoreFlag,
   242  			configdirFlag,
   243  			chainIdFlag,
   244  			utils.LightKDFFlag,
   245  			utils.NoUSBFlag,
   246  			utils.SmartCardDaemonPathFlag,
   247  			utils.HTTPListenAddrFlag,
   248  			utils.HTTPVirtualHostsFlag,
   249  			utils.IPCDisabledFlag,
   250  			utils.IPCPathFlag,
   251  			utils.HTTPEnabledFlag,
   252  			rpcPortFlag,
   253  			signerSecretFlag,
   254  			customDBFlag,
   255  			auditLogFlag,
   256  			ruleFlag,
   257  			stdiouiFlag,
   258  			testFlag,
   259  			advancedMode,
   260  			acceptFlag,
   261  			// <Quorum>
   262  			utils.PluginSettingsFlag,
   263  			utils.PluginLocalVerifyFlag,
   264  			utils.PluginPublicKeyFlag,
   265  			utils.PluginSkipVerifyFlag,
   266  			// </Quorum>
   267  		},
   268  	},
   269  	{
   270  		Name: "ALIASED (deprecated)",
   271  		Flags: []cli.Flag{
   272  			legacyRPCPortFlag,
   273  		},
   274  	},
   275  }
   276  
   277  func init() {
   278  	app.Name = "Clef"
   279  	app.Usage = "Manage Ethereum account operations"
   280  	app.Flags = []cli.Flag{
   281  		logLevelFlag,
   282  		keystoreFlag,
   283  		configdirFlag,
   284  		chainIdFlag,
   285  		utils.LightKDFFlag,
   286  		utils.NoUSBFlag,
   287  		utils.SmartCardDaemonPathFlag,
   288  		utils.HTTPListenAddrFlag,
   289  		utils.HTTPVirtualHostsFlag,
   290  		utils.IPCDisabledFlag,
   291  		utils.IPCPathFlag,
   292  		utils.HTTPEnabledFlag,
   293  		rpcPortFlag,
   294  		signerSecretFlag,
   295  		customDBFlag,
   296  		auditLogFlag,
   297  		ruleFlag,
   298  		stdiouiFlag,
   299  		testFlag,
   300  		advancedMode,
   301  		acceptFlag,
   302  		legacyRPCPortFlag,
   303  		// <Quorum>
   304  		utils.PluginSettingsFlag,
   305  		utils.PluginLocalVerifyFlag,
   306  		utils.PluginPublicKeyFlag,
   307  		utils.PluginSkipVerifyFlag,
   308  		// </Quorum>
   309  	}
   310  	app.Action = signer
   311  	app.Commands = []cli.Command{initCommand,
   312  		attestCommand,
   313  		setCredentialCommand,
   314  		delCredentialCommand,
   315  		newAccountCommand,
   316  		gendocCommand}
   317  	cli.CommandHelpTemplate = flags.CommandHelpTemplate
   318  	// Override the default app help template
   319  	cli.AppHelpTemplate = flags.ClefAppHelpTemplate
   320  
   321  	// Override the default app help printer, but only for the global app help
   322  	originalHelpPrinter := cli.HelpPrinter
   323  	cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) {
   324  		if tmpl == flags.ClefAppHelpTemplate {
   325  			// Render out custom usage screen
   326  			originalHelpPrinter(w, tmpl, flags.HelpData{App: data, FlagGroups: AppHelpFlagGroups})
   327  		} else if tmpl == flags.CommandHelpTemplate {
   328  			// Iterate over all command specific flags and categorize them
   329  			categorized := make(map[string][]cli.Flag)
   330  			for _, flag := range data.(cli.Command).Flags {
   331  				if _, ok := categorized[flag.String()]; !ok {
   332  					categorized[flags.FlagCategory(flag, AppHelpFlagGroups)] = append(categorized[flags.FlagCategory(flag, AppHelpFlagGroups)], flag)
   333  				}
   334  			}
   335  
   336  			// sort to get a stable ordering
   337  			sorted := make([]flags.FlagGroup, 0, len(categorized))
   338  			for cat, flgs := range categorized {
   339  				sorted = append(sorted, flags.FlagGroup{Name: cat, Flags: flgs})
   340  			}
   341  			sort.Sort(flags.ByCategory(sorted))
   342  
   343  			// add sorted array to data and render with default printer
   344  			originalHelpPrinter(w, tmpl, map[string]interface{}{
   345  				"cmd":              data,
   346  				"categorizedFlags": sorted,
   347  			})
   348  		} else {
   349  			originalHelpPrinter(w, tmpl, data)
   350  		}
   351  	}
   352  }
   353  
   354  func main() {
   355  	if err := app.Run(os.Args); err != nil {
   356  		fmt.Fprintln(os.Stderr, err)
   357  		os.Exit(1)
   358  	}
   359  }
   360  
   361  func initializeSecrets(c *cli.Context) error {
   362  	// Get past the legal message
   363  	if err := initialize(c); err != nil {
   364  		return err
   365  	}
   366  	// Ensure the master key does not yet exist, we're not willing to overwrite
   367  	configDir := c.GlobalString(configdirFlag.Name)
   368  	if err := os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) {
   369  		return err
   370  	}
   371  	location := filepath.Join(configDir, "masterseed.json")
   372  	if _, err := os.Stat(location); err == nil {
   373  		return fmt.Errorf("master key %v already exists, will not overwrite", location)
   374  	}
   375  	// Key file does not exist yet, generate a new one and encrypt it
   376  	masterSeed := make([]byte, 256)
   377  	num, err := io.ReadFull(rand.Reader, masterSeed)
   378  	if err != nil {
   379  		return err
   380  	}
   381  	if num != len(masterSeed) {
   382  		return fmt.Errorf("failed to read enough random")
   383  	}
   384  	n, p := keystore.StandardScryptN, keystore.StandardScryptP
   385  	if c.GlobalBool(utils.LightKDFFlag.Name) {
   386  		n, p = keystore.LightScryptN, keystore.LightScryptP
   387  	}
   388  	text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!"
   389  	var password string
   390  	for {
   391  		password = utils.GetPassPhrase(text, true)
   392  		if err := core.ValidatePasswordFormat(password); err != nil {
   393  			fmt.Printf("invalid password: %v\n", err)
   394  		} else {
   395  			fmt.Println()
   396  			break
   397  		}
   398  	}
   399  	cipherSeed, err := encryptSeed(masterSeed, []byte(password), n, p)
   400  	if err != nil {
   401  		return fmt.Errorf("failed to encrypt master seed: %v", err)
   402  	}
   403  	// Double check the master key path to ensure nothing wrote there in between
   404  	if err = os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) {
   405  		return err
   406  	}
   407  	if _, err := os.Stat(location); err == nil {
   408  		return fmt.Errorf("master key %v already exists, will not overwrite", location)
   409  	}
   410  	// Write the file and print the usual warning message
   411  	if err = ioutil.WriteFile(location, cipherSeed, 0400); err != nil {
   412  		return err
   413  	}
   414  	fmt.Printf("A master seed has been generated into %s\n", location)
   415  	fmt.Printf(`
   416  This is required to be able to store credentials, such as:
   417  * Passwords for keystores (used by rule engine)
   418  * Storage for JavaScript auto-signing rules
   419  * Hash of JavaScript rule-file
   420  
   421  You should treat 'masterseed.json' with utmost secrecy and make a backup of it!
   422  * The password is necessary but not enough, you need to back up the master seed too!
   423  * The master seed does not contain your accounts, those need to be backed up separately!
   424  
   425  `)
   426  	return nil
   427  }
   428  func attestFile(ctx *cli.Context) error {
   429  	if len(ctx.Args()) < 1 {
   430  		utils.Fatalf("This command requires an argument.")
   431  	}
   432  	if err := initialize(ctx); err != nil {
   433  		return err
   434  	}
   435  
   436  	stretchedKey, err := readMasterKey(ctx, nil)
   437  	if err != nil {
   438  		utils.Fatalf(err.Error())
   439  	}
   440  	configDir := ctx.GlobalString(configdirFlag.Name)
   441  	vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
   442  	confKey := crypto.Keccak256([]byte("config"), stretchedKey)
   443  
   444  	// Initialize the encrypted storages
   445  	configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confKey)
   446  	val := ctx.Args().First()
   447  	configStorage.Put("ruleset_sha256", val)
   448  	log.Info("Ruleset attestation updated", "sha256", val)
   449  	return nil
   450  }
   451  
   452  func setCredential(ctx *cli.Context) error {
   453  	if len(ctx.Args()) < 1 {
   454  		utils.Fatalf("This command requires an address to be passed as an argument")
   455  	}
   456  	if err := initialize(ctx); err != nil {
   457  		return err
   458  	}
   459  	addr := ctx.Args().First()
   460  	if !common.IsHexAddress(addr) {
   461  		utils.Fatalf("Invalid address specified: %s", addr)
   462  	}
   463  	address := common.HexToAddress(addr)
   464  	password := utils.GetPassPhrase("Please enter a password to store for this address:", true)
   465  	fmt.Println()
   466  
   467  	stretchedKey, err := readMasterKey(ctx, nil)
   468  	if err != nil {
   469  		utils.Fatalf(err.Error())
   470  	}
   471  	configDir := ctx.GlobalString(configdirFlag.Name)
   472  	vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
   473  	pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
   474  
   475  	pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
   476  	pwStorage.Put(address.Hex(), password)
   477  
   478  	log.Info("Credential store updated", "set", address)
   479  	return nil
   480  }
   481  
   482  func removeCredential(ctx *cli.Context) error {
   483  	if len(ctx.Args()) < 1 {
   484  		utils.Fatalf("This command requires an address to be passed as an argument")
   485  	}
   486  	if err := initialize(ctx); err != nil {
   487  		return err
   488  	}
   489  	addr := ctx.Args().First()
   490  	if !common.IsHexAddress(addr) {
   491  		utils.Fatalf("Invalid address specified: %s", addr)
   492  	}
   493  	address := common.HexToAddress(addr)
   494  
   495  	stretchedKey, err := readMasterKey(ctx, nil)
   496  	if err != nil {
   497  		utils.Fatalf(err.Error())
   498  	}
   499  	configDir := ctx.GlobalString(configdirFlag.Name)
   500  	vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
   501  	pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
   502  
   503  	pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
   504  	pwStorage.Del(address.Hex())
   505  
   506  	log.Info("Credential store updated", "unset", address)
   507  	return nil
   508  }
   509  
   510  func newAccount(c *cli.Context) error {
   511  	if err := initialize(c); err != nil {
   512  		return err
   513  	}
   514  	// The newaccount is meant for users using the CLI, since 'real' external
   515  	// UIs can use the UI-api instead. So we'll just use the native CLI UI here.
   516  	var (
   517  		ui                        = core.NewCommandlineUI()
   518  		pwStorage storage.Storage = &storage.NoStorage{}
   519  		ksLoc                     = c.GlobalString(keystoreFlag.Name)
   520  		lightKdf                  = c.GlobalBool(utils.LightKDFFlag.Name)
   521  	)
   522  	log.Info("Starting clef", "keystore", ksLoc, "light-kdf", lightKdf)
   523  	am, _, err := startClefAccountManagerWithPlugins(c, ksLoc, true, lightKdf, "")
   524  	if err != nil {
   525  		return err
   526  	}
   527  	// This gives is us access to the external API
   528  	apiImpl := core.NewSignerAPI(am, 0, true, ui, nil, false, pwStorage)
   529  	// This gives us access to the internal API
   530  	internalApi := core.NewUIServerAPI(apiImpl)
   531  	addr, err := internalApi.New(context.Background())
   532  	if err == nil {
   533  		fmt.Printf("Generated account %v\n", addr.String())
   534  	}
   535  	return err
   536  }
   537  
   538  func initialize(c *cli.Context) error {
   539  	// Set up the logger to print everything
   540  	logOutput := os.Stdout
   541  	if c.GlobalBool(stdiouiFlag.Name) {
   542  		logOutput = os.Stderr
   543  		// If using the stdioui, we can't do the 'confirm'-flow
   544  		if !c.GlobalBool(acceptFlag.Name) {
   545  			fmt.Fprint(logOutput, legalWarning)
   546  		}
   547  	} else if !c.GlobalBool(acceptFlag.Name) {
   548  		if !confirm(legalWarning) {
   549  			return fmt.Errorf("aborted by user")
   550  		}
   551  		fmt.Println()
   552  	}
   553  	usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
   554  	output := io.Writer(logOutput)
   555  	if usecolor {
   556  		output = colorable.NewColorable(logOutput)
   557  	}
   558  	log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(output, log.TerminalFormat(usecolor))))
   559  
   560  	return nil
   561  }
   562  
   563  // ipcEndpoint resolves an IPC endpoint based on a configured value, taking into
   564  // account the set data folders as well as the designated platform we're currently
   565  // running on.
   566  func ipcEndpoint(ipcPath, datadir string) string {
   567  	// On windows we can only use plain top-level pipes
   568  	if runtime.GOOS == "windows" {
   569  		if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
   570  			return ipcPath
   571  		}
   572  		return `\\.\pipe\` + ipcPath
   573  	}
   574  	// Resolve names into the data directory full paths otherwise
   575  	if filepath.Base(ipcPath) == ipcPath {
   576  		if datadir == "" {
   577  			return filepath.Join(os.TempDir(), ipcPath)
   578  		}
   579  		return filepath.Join(datadir, ipcPath)
   580  	}
   581  	return ipcPath
   582  }
   583  
   584  func signer(c *cli.Context) error {
   585  	// If we have some unrecognized command, bail out
   586  	if args := c.Args(); len(args) > 0 {
   587  		return fmt.Errorf("invalid command: %q", args[0])
   588  	}
   589  	if err := initialize(c); err != nil {
   590  		return err
   591  	}
   592  	var (
   593  		ui core.UIClientAPI
   594  	)
   595  	if c.GlobalBool(stdiouiFlag.Name) {
   596  		log.Info("Using stdin/stdout as UI-channel")
   597  		ui = core.NewStdIOUI()
   598  	} else {
   599  		log.Info("Using CLI as UI-channel")
   600  		ui = core.NewCommandlineUI()
   601  	}
   602  	// 4bytedb data
   603  	fourByteLocal := c.GlobalString(customDBFlag.Name)
   604  	db, err := fourbyte.NewWithFile(fourByteLocal)
   605  	if err != nil {
   606  		utils.Fatalf(err.Error())
   607  	}
   608  	embeds, locals := db.Size()
   609  	log.Info("Loaded 4byte database", "embeds", embeds, "locals", locals, "local", fourByteLocal)
   610  
   611  	var (
   612  		api       core.ExternalAPI
   613  		pwStorage storage.Storage = &storage.NoStorage{}
   614  	)
   615  	configDir := c.GlobalString(configdirFlag.Name)
   616  	if stretchedKey, err := readMasterKey(c, ui); err != nil {
   617  		log.Warn("Failed to open master, rules disabled", "err", err)
   618  	} else {
   619  		vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
   620  
   621  		// Generate domain specific keys
   622  		pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey)
   623  		jskey := crypto.Keccak256([]byte("jsstorage"), stretchedKey)
   624  		confkey := crypto.Keccak256([]byte("config"), stretchedKey)
   625  
   626  		// Initialize the encrypted storages
   627  		pwStorage = storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
   628  		jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey)
   629  		configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey)
   630  
   631  		// Do we have a rule-file?
   632  		if ruleFile := c.GlobalString(ruleFlag.Name); ruleFile != "" {
   633  			ruleJS, err := ioutil.ReadFile(ruleFile)
   634  			if err != nil {
   635  				log.Warn("Could not load rules, disabling", "file", ruleFile, "err", err)
   636  			} else {
   637  				shasum := sha256.Sum256(ruleJS)
   638  				foundShaSum := hex.EncodeToString(shasum[:])
   639  				storedShasum, _ := configStorage.Get("ruleset_sha256")
   640  				if storedShasum != foundShaSum {
   641  					log.Warn("Rule hash not attested, disabling", "hash", foundShaSum, "attested", storedShasum)
   642  				} else {
   643  					// Initialize rules
   644  					ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage)
   645  					if err != nil {
   646  						utils.Fatalf(err.Error())
   647  					}
   648  					ruleEngine.Init(string(ruleJS))
   649  					ui = ruleEngine
   650  					log.Info("Rule engine configured", "file", c.String(ruleFlag.Name))
   651  				}
   652  			}
   653  		}
   654  	}
   655  	var (
   656  		chainId  = c.GlobalInt64(chainIdFlag.Name)
   657  		ksLoc    = c.GlobalString(keystoreFlag.Name)
   658  		lightKdf = c.GlobalBool(utils.LightKDFFlag.Name)
   659  		advanced = c.GlobalBool(advancedMode.Name)
   660  		nousb    = c.GlobalBool(utils.NoUSBFlag.Name)
   661  		scpath   = c.GlobalString(utils.SmartCardDaemonPathFlag.Name)
   662  	)
   663  	log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc,
   664  		"light-kdf", lightKdf, "advanced", advanced)
   665  
   666  	am, pm, err := startClefAccountManagerWithPlugins(c, ksLoc, nousb, lightKdf, scpath)
   667  	if err != nil {
   668  		return err
   669  	}
   670  
   671  	apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage)
   672  
   673  	// Establish the bidirectional communication, by creating a new UI backend and registering
   674  	// it with the UI.
   675  	ui.RegisterUIServer(core.NewUIServerAPI(apiImpl))
   676  	api = apiImpl
   677  	// Audit logging
   678  	if logfile := c.GlobalString(auditLogFlag.Name); logfile != "" {
   679  		api, err = core.NewAuditLogger(logfile, api)
   680  		if err != nil {
   681  			utils.Fatalf(err.Error())
   682  		}
   683  		log.Info("Audit logs configured", "file", logfile)
   684  	}
   685  	// register signer API with server
   686  	var (
   687  		extapiURL = "n/a"
   688  		ipcapiURL = "n/a"
   689  	)
   690  	rpcAPI := []rpc.API{
   691  		{
   692  			Namespace: "account",
   693  			Public:    true,
   694  			Service:   api,
   695  			Version:   "1.0"},
   696  	}
   697  
   698  	// <Quorum>
   699  	if pm != nil {
   700  		rpcAPI = addPluginAPIs(pm, rpcAPI, ui)
   701  	}
   702  	// </Quorum>
   703  
   704  	if c.GlobalBool(utils.HTTPEnabledFlag.Name) {
   705  		vhosts := utils.SplitAndTrim(c.GlobalString(utils.HTTPVirtualHostsFlag.Name))
   706  		cors := utils.SplitAndTrim(c.GlobalString(utils.HTTPCORSDomainFlag.Name))
   707  
   708  		srv := rpc.NewServer()
   709  		err := node.RegisterApisFromWhitelist(rpcAPI, []string{"account"}, srv, false)
   710  		if err != nil {
   711  			utils.Fatalf("Could not register API: %w", err)
   712  		}
   713  		handler := node.NewHTTPHandlerStack(srv, cors, vhosts)
   714  
   715  		// set port
   716  		port := c.Int(rpcPortFlag.Name)
   717  		if c.GlobalIsSet(legacyRPCPortFlag.Name) {
   718  			if !c.GlobalIsSet(rpcPortFlag.Name) {
   719  				port = c.Int(legacyRPCPortFlag.Name)
   720  			}
   721  			log.Warn("The flag --rpcport is deprecated and will be removed in the future, please use --http.port")
   722  		}
   723  
   724  		// start http server
   725  		httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.HTTPListenAddrFlag.Name), port)
   726  		httpServer, addr, _, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler, nil)
   727  		if err != nil {
   728  			utils.Fatalf("Could not start RPC api: %v", err)
   729  		}
   730  		extapiURL = fmt.Sprintf("http://%v/", addr)
   731  		log.Info("HTTP endpoint opened", "url", extapiURL)
   732  
   733  		defer func() {
   734  			// Don't bother imposing a timeout here.
   735  			httpServer.Shutdown(context.Background())
   736  			log.Info("HTTP endpoint closed", "url", extapiURL)
   737  		}()
   738  	}
   739  	if !c.GlobalBool(utils.IPCDisabledFlag.Name) {
   740  		givenPath := c.GlobalString(utils.IPCPathFlag.Name)
   741  		ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir)
   742  		listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI)
   743  		if err != nil {
   744  			utils.Fatalf("Could not start IPC api: %v", err)
   745  		}
   746  		log.Info("IPC endpoint opened", "url", ipcapiURL)
   747  		defer func() {
   748  			listener.Close()
   749  			log.Info("IPC endpoint closed", "url", ipcapiURL)
   750  		}()
   751  	}
   752  
   753  	if c.GlobalBool(testFlag.Name) {
   754  		log.Info("Performing UI test")
   755  		go testExternalUI(apiImpl)
   756  	}
   757  	ui.OnSignerStartup(core.StartupInfo{
   758  		Info: map[string]interface{}{
   759  			"intapi_version": core.InternalAPIVersion,
   760  			"extapi_version": core.ExternalAPIVersion,
   761  			"extapi_http":    extapiURL,
   762  			"extapi_ipc":     ipcapiURL,
   763  		},
   764  	})
   765  
   766  	abortChan := make(chan os.Signal, 1)
   767  	signal.Notify(abortChan, os.Interrupt)
   768  
   769  	sig := <-abortChan
   770  	log.Info("Exiting...", "signal", sig)
   771  
   772  	return nil
   773  }
   774  
   775  // <Quorum/>
   776  // startPluginManager gets plugin config and starts a new PluginManager
   777  func startPluginManager(c *cli.Context) (*plugin.PluginManager, *plugin.Settings, error) {
   778  	nodeConf := new(node.Config)
   779  	if err := utils.SetPlugins(c, nodeConf); err != nil {
   780  		return nil, nil, err
   781  	}
   782  	pluginConf := nodeConf.Plugins
   783  
   784  	if err := pluginConf.CheckSettingsAreSupported(supportedPlugins); err != nil {
   785  		return nil, nil, err
   786  	}
   787  
   788  	pm, err := plugin.NewPluginManager("clef", pluginConf, c.Bool(utils.PluginSkipVerifyFlag.Name), c.Bool(utils.PluginLocalVerifyFlag.Name), c.String(utils.PluginPublicKeyFlag.Name))
   789  	if err != nil {
   790  		return nil, nil, err
   791  	}
   792  	if err := pm.Start(); err != nil {
   793  		return nil, nil, err
   794  	}
   795  	return pm, pluginConf, nil
   796  }
   797  
   798  // addPluginAPIs adds the exposed plugin APIs to Clef's API.
   799  // It alters some of the plugin APIs so that calls to them will require UI approval.
   800  func addPluginAPIs(pm *plugin.PluginManager, rpcAPI []rpc.API, ui core.UIClientAPI) []rpc.API {
   801  	// pm.APIs() returns a slice of values hence the following approach that may look clumsy
   802  	var (
   803  		stdPluginAPIs      = pm.APIs()
   804  		approvalPluginAPIs = make([]rpc.API, len(stdPluginAPIs))
   805  	)
   806  
   807  	for i, api := range stdPluginAPIs {
   808  		switch s := api.Service.(type) {
   809  		case account.CreatorService:
   810  			api.Service = core.NewApprovalCreatorService(s, ui)
   811  		}
   812  		approvalPluginAPIs[i] = api
   813  	}
   814  	return append(rpcAPI, approvalPluginAPIs...)
   815  }
   816  
   817  // DefaultConfigDir is the default config directory to use for the vaults and other
   818  // persistence requirements.
   819  func DefaultConfigDir() string {
   820  	// Try to place the data folder in the user's home dir
   821  	home := utils.HomeDir()
   822  	if home != "" {
   823  		if runtime.GOOS == "darwin" {
   824  			return filepath.Join(home, "Library", "Signer")
   825  		} else if runtime.GOOS == "windows" {
   826  			appdata := os.Getenv("APPDATA")
   827  			if appdata != "" {
   828  				return filepath.Join(appdata, "Signer")
   829  			}
   830  			return filepath.Join(home, "AppData", "Roaming", "Signer")
   831  		}
   832  		return filepath.Join(home, ".clef")
   833  	}
   834  	// As we cannot guess a stable location, return empty and handle later
   835  	return ""
   836  }
   837  
   838  func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) {
   839  	var (
   840  		file      string
   841  		configDir = ctx.GlobalString(configdirFlag.Name)
   842  	)
   843  	if ctx.GlobalIsSet(signerSecretFlag.Name) {
   844  		file = ctx.GlobalString(signerSecretFlag.Name)
   845  	} else {
   846  		file = filepath.Join(configDir, "masterseed.json")
   847  	}
   848  	if err := checkFile(file); err != nil {
   849  		return nil, err
   850  	}
   851  	cipherKey, err := ioutil.ReadFile(file)
   852  	if err != nil {
   853  		return nil, err
   854  	}
   855  	var password string
   856  	// If ui is not nil, get the password from ui.
   857  	if ui != nil {
   858  		resp, err := ui.OnInputRequired(core.UserInputRequest{
   859  			Title:      "Master Password",
   860  			Prompt:     "Please enter the password to decrypt the master seed",
   861  			IsPassword: true})
   862  		if err != nil {
   863  			return nil, err
   864  		}
   865  		password = resp.Text
   866  	} else {
   867  		password = utils.GetPassPhrase("Decrypt master seed of clef", false)
   868  	}
   869  	masterSeed, err := decryptSeed(cipherKey, password)
   870  	if err != nil {
   871  		return nil, fmt.Errorf("failed to decrypt the master seed of clef")
   872  	}
   873  	if len(masterSeed) < 256 {
   874  		return nil, fmt.Errorf("master seed of insufficient length, expected >255 bytes, got %d", len(masterSeed))
   875  	}
   876  	// Create vault location
   877  	vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterSeed)[:10]))
   878  	err = os.Mkdir(vaultLocation, 0700)
   879  	if err != nil && !os.IsExist(err) {
   880  		return nil, err
   881  	}
   882  	return masterSeed, nil
   883  }
   884  
   885  // checkFile is a convenience function to check if a file
   886  // * exists
   887  // * is mode 0400
   888  func checkFile(filename string) error {
   889  	info, err := os.Stat(filename)
   890  	if err != nil {
   891  		return fmt.Errorf("failed stat on %s: %v", filename, err)
   892  	}
   893  	// Check the unix permission bits
   894  	if info.Mode().Perm()&0377 != 0 {
   895  		return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String())
   896  	}
   897  	return nil
   898  }
   899  
   900  // confirm displays a text and asks for user confirmation
   901  func confirm(text string) bool {
   902  	fmt.Print(text)
   903  	fmt.Printf("\nEnter 'ok' to proceed:\n> ")
   904  
   905  	text, err := bufio.NewReader(os.Stdin).ReadString('\n')
   906  	if err != nil {
   907  		log.Crit("Failed to read user input", "err", err)
   908  	}
   909  	if text := strings.TrimSpace(text); text == "ok" {
   910  		return true
   911  	}
   912  	return false
   913  }
   914  
   915  func testExternalUI(api *core.SignerAPI) {
   916  
   917  	ctx := context.WithValue(context.Background(), "remote", "clef binary")
   918  	ctx = context.WithValue(ctx, "scheme", "in-proc")
   919  	ctx = context.WithValue(ctx, "local", "main")
   920  	errs := make([]string, 0)
   921  
   922  	a := common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef")
   923  	addErr := func(errStr string) {
   924  		log.Info("Test error", "err", errStr)
   925  		errs = append(errs, errStr)
   926  	}
   927  
   928  	queryUser := func(q string) string {
   929  		resp, err := api.UI.OnInputRequired(core.UserInputRequest{
   930  			Title:  "Testing",
   931  			Prompt: q,
   932  		})
   933  		if err != nil {
   934  			addErr(err.Error())
   935  		}
   936  		return resp.Text
   937  	}
   938  	expectResponse := func(testcase, question, expect string) {
   939  		if got := queryUser(question); got != expect {
   940  			addErr(fmt.Sprintf("%s: got %v, expected %v", testcase, got, expect))
   941  		}
   942  	}
   943  	expectApprove := func(testcase string, err error) {
   944  		if err == nil || err == accounts.ErrUnknownAccount {
   945  			return
   946  		}
   947  		addErr(fmt.Sprintf("%v: expected no error, got %v", testcase, err.Error()))
   948  	}
   949  	expectDeny := func(testcase string, err error) {
   950  		if err == nil || err != core.ErrRequestDenied {
   951  			addErr(fmt.Sprintf("%v: expected ErrRequestDenied, got %v", testcase, err))
   952  		}
   953  	}
   954  	var delay = 1 * time.Second
   955  	// Test display of info and error
   956  	{
   957  		api.UI.ShowInfo("If you see this message, enter 'yes' to next question")
   958  		time.Sleep(delay)
   959  		expectResponse("showinfo", "Did you see the message? [yes/no]", "yes")
   960  		api.UI.ShowError("If you see this message, enter 'yes' to the next question")
   961  		time.Sleep(delay)
   962  		expectResponse("showerror", "Did you see the message? [yes/no]", "yes")
   963  	}
   964  	{ // Sign data test - clique header
   965  		api.UI.ShowInfo("Please approve the next request for signing a clique header")
   966  		time.Sleep(delay)
   967  		cliqueHeader := types.Header{
   968  			ParentHash:  common.HexToHash("0000H45H"),
   969  			UncleHash:   common.HexToHash("0000H45H"),
   970  			Coinbase:    common.HexToAddress("0000H45H"),
   971  			Root:        common.HexToHash("0000H00H"),
   972  			TxHash:      common.HexToHash("0000H45H"),
   973  			ReceiptHash: common.HexToHash("0000H45H"),
   974  			Difficulty:  big.NewInt(1337),
   975  			Number:      big.NewInt(1337),
   976  			GasLimit:    1338,
   977  			GasUsed:     1338,
   978  			Time:        1338,
   979  			Extra:       []byte("Extra data Extra data Extra data  Extra data  Extra data  Extra data  Extra data Extra data"),
   980  			MixDigest:   common.HexToHash("0x0000H45H"),
   981  		}
   982  		cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
   983  		if err != nil {
   984  			utils.Fatalf("Should not error: %v", err)
   985  		}
   986  		addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
   987  		_, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp))
   988  		expectApprove("signdata - clique header", err)
   989  	}
   990  	{ // Sign data test - typed data
   991  		api.UI.ShowInfo("Please approve the next request for signing EIP-712 typed data")
   992  		time.Sleep(delay)
   993  		addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
   994  		data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}`
   995  		//_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data)))
   996  		var typedData core.TypedData
   997  		json.Unmarshal([]byte(data), &typedData)
   998  		_, err := api.SignTypedData(ctx, *addr, typedData)
   999  		expectApprove("sign 712 typed data", err)
  1000  	}
  1001  	{ // Sign data test - plain text
  1002  		api.UI.ShowInfo("Please approve the next request for signing text")
  1003  		time.Sleep(delay)
  1004  		addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
  1005  		_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
  1006  		expectApprove("signdata - text", err)
  1007  	}
  1008  	{ // Sign data test - plain text reject
  1009  		api.UI.ShowInfo("Please deny the next request for signing text")
  1010  		time.Sleep(delay)
  1011  		addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
  1012  		_, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world")))
  1013  		expectDeny("signdata - text", err)
  1014  	}
  1015  	{ // Sign transaction
  1016  
  1017  		api.UI.ShowInfo("Please reject next transaction")
  1018  		time.Sleep(delay)
  1019  		data := hexutil.Bytes([]byte{})
  1020  		to := common.NewMixedcaseAddress(a)
  1021  		tx := core.SendTxArgs{
  1022  			Data:     &data,
  1023  			Nonce:    0x1,
  1024  			Value:    hexutil.Big(*big.NewInt(6)),
  1025  			From:     common.NewMixedcaseAddress(a),
  1026  			To:       &to,
  1027  			GasPrice: hexutil.Big(*big.NewInt(5)),
  1028  			Gas:      1000,
  1029  			Input:    nil,
  1030  		}
  1031  		_, err := api.SignTransaction(ctx, tx, nil)
  1032  		expectDeny("signtransaction [1]", err)
  1033  		expectResponse("signtransaction [2]", "Did you see any warnings for the last transaction? (yes/no)", "no")
  1034  	}
  1035  	{ // Listing
  1036  		api.UI.ShowInfo("Please reject listing-request")
  1037  		time.Sleep(delay)
  1038  		_, err := api.List(ctx)
  1039  		expectDeny("list", err)
  1040  	}
  1041  	{ // Import
  1042  		api.UI.ShowInfo("Please reject new account-request")
  1043  		time.Sleep(delay)
  1044  		_, err := api.New(ctx)
  1045  		expectDeny("newaccount", err)
  1046  	}
  1047  	{ // Metadata
  1048  		api.UI.ShowInfo("Please check if you see the Origin in next listing (approve or deny)")
  1049  		time.Sleep(delay)
  1050  		api.List(context.WithValue(ctx, "Origin", "origin.com"))
  1051  		expectResponse("metadata - origin", "Did you see origin (origin.com)? [yes/no] ", "yes")
  1052  	}
  1053  
  1054  	for _, e := range errs {
  1055  		log.Error(e)
  1056  	}
  1057  	result := fmt.Sprintf("Tests completed. %d errors:\n%s\n", len(errs), strings.Join(errs, "\n"))
  1058  	api.UI.ShowInfo(result)
  1059  
  1060  }
  1061  
  1062  type encryptedSeedStorage struct {
  1063  	Description string              `json:"description"`
  1064  	Version     int                 `json:"version"`
  1065  	Params      keystore.CryptoJSON `json:"params"`
  1066  }
  1067  
  1068  // encryptSeed uses a similar scheme as the keystore uses, but with a different wrapping,
  1069  // to encrypt the master seed
  1070  func encryptSeed(seed []byte, auth []byte, scryptN, scryptP int) ([]byte, error) {
  1071  	cryptoStruct, err := keystore.EncryptDataV3(seed, auth, scryptN, scryptP)
  1072  	if err != nil {
  1073  		return nil, err
  1074  	}
  1075  	return json.Marshal(&encryptedSeedStorage{"Clef seed", 1, cryptoStruct})
  1076  }
  1077  
  1078  // decryptSeed decrypts the master seed
  1079  func decryptSeed(keyjson []byte, auth string) ([]byte, error) {
  1080  	var encSeed encryptedSeedStorage
  1081  	if err := json.Unmarshal(keyjson, &encSeed); err != nil {
  1082  		return nil, err
  1083  	}
  1084  	if encSeed.Version != 1 {
  1085  		log.Warn(fmt.Sprintf("unsupported encryption format of seed: %d, operation will likely fail", encSeed.Version))
  1086  	}
  1087  	seed, err := keystore.DecryptDataV3(encSeed.Params, auth)
  1088  	if err != nil {
  1089  		return nil, err
  1090  	}
  1091  	return seed, err
  1092  }
  1093  
  1094  // GenDoc outputs examples of all structures used in json-rpc communication
  1095  func GenDoc(ctx *cli.Context) {
  1096  
  1097  	var (
  1098  		a    = common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef")
  1099  		b    = common.HexToAddress("0x1111111122222222222233333333334444444444")
  1100  		meta = core.Metadata{
  1101  			Scheme:    "http",
  1102  			Local:     "localhost:8545",
  1103  			Origin:    "www.malicious.ru",
  1104  			Remote:    "localhost:9999",
  1105  			UserAgent: "Firefox 3.2",
  1106  		}
  1107  		output []string
  1108  		add    = func(name, desc string, v interface{}) {
  1109  			if data, err := json.MarshalIndent(v, "", "  "); err == nil {
  1110  				output = append(output, fmt.Sprintf("### %s\n\n%s\n\nExample:\n```json\n%s\n```", name, desc, data))
  1111  			} else {
  1112  				log.Error("Error generating output", "err", err)
  1113  			}
  1114  		}
  1115  	)
  1116  
  1117  	{ // Sign plain text request
  1118  		desc := "SignDataRequest contains information about a pending request to sign some data. " +
  1119  			"The data to be signed can be of various types, defined by content-type. Clef has done most " +
  1120  			"of the work in canonicalizing and making sense of the data, and it's up to the UI to present" +
  1121  			"the user with the contents of the `message`"
  1122  		sighash, msg := accounts.TextAndHash([]byte("hello world"))
  1123  		messages := []*core.NameValueType{{Name: "message", Value: msg, Typ: accounts.MimetypeTextPlain}}
  1124  
  1125  		add("SignDataRequest", desc, &core.SignDataRequest{
  1126  			Address:     common.NewMixedcaseAddress(a),
  1127  			Meta:        meta,
  1128  			ContentType: accounts.MimetypeTextPlain,
  1129  			Rawdata:     []byte(msg),
  1130  			Messages:    messages,
  1131  			Hash:        sighash})
  1132  	}
  1133  	{ // Sign plain text response
  1134  		add("SignDataResponse - approve", "Response to SignDataRequest",
  1135  			&core.SignDataResponse{Approved: true})
  1136  		add("SignDataResponse - deny", "Response to SignDataRequest",
  1137  			&core.SignDataResponse{})
  1138  	}
  1139  	{ // Sign transaction request
  1140  		desc := "SignTxRequest contains information about a pending request to sign a transaction. " +
  1141  			"Aside from the transaction itself, there is also a `call_info`-struct. That struct contains " +
  1142  			"messages of various types, that the user should be informed of." +
  1143  			"\n\n" +
  1144  			"As in any request, it's important to consider that the `meta` info also contains untrusted data." +
  1145  			"\n\n" +
  1146  			"The `transaction` (on input into clef) can have either `data` or `input` -- if both are set, " +
  1147  			"they must be identical, otherwise an error is generated. " +
  1148  			"However, Clef will always use `data` when passing this struct on (if Clef does otherwise, please file a ticket)"
  1149  
  1150  		data := hexutil.Bytes([]byte{0x01, 0x02, 0x03, 0x04})
  1151  		add("SignTxRequest", desc, &core.SignTxRequest{
  1152  			Meta: meta,
  1153  			Callinfo: []core.ValidationInfo{
  1154  				{Typ: "Warning", Message: "Something looks odd, show this message as a warning"},
  1155  				{Typ: "Info", Message: "User should see this as well"},
  1156  			},
  1157  			Transaction: core.SendTxArgs{
  1158  				Data:     &data,
  1159  				Nonce:    0x1,
  1160  				Value:    hexutil.Big(*big.NewInt(6)),
  1161  				From:     common.NewMixedcaseAddress(a),
  1162  				To:       nil,
  1163  				GasPrice: hexutil.Big(*big.NewInt(5)),
  1164  				Gas:      1000,
  1165  				Input:    nil,
  1166  			}})
  1167  	}
  1168  	{ // Sign tx response
  1169  		data := hexutil.Bytes([]byte{0x04, 0x03, 0x02, 0x01})
  1170  		add("SignTxResponse - approve", "Response to request to sign a transaction. This response needs to contain the `transaction`"+
  1171  			", because the UI is free to make modifications to the transaction.",
  1172  			&core.SignTxResponse{Approved: true,
  1173  				Transaction: core.SendTxArgs{
  1174  					Data:     &data,
  1175  					Nonce:    0x4,
  1176  					Value:    hexutil.Big(*big.NewInt(6)),
  1177  					From:     common.NewMixedcaseAddress(a),
  1178  					To:       nil,
  1179  					GasPrice: hexutil.Big(*big.NewInt(5)),
  1180  					Gas:      1000,
  1181  					Input:    nil,
  1182  				}})
  1183  		add("SignTxResponse - deny", "Response to SignTxRequest. When denying a request, there's no need to "+
  1184  			"provide the transaction in return",
  1185  			&core.SignTxResponse{})
  1186  	}
  1187  	{ // WHen a signed tx is ready to go out
  1188  		desc := "SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)`" +
  1189  			"\n\n" +
  1190  			"This occurs _after_ successful completion of the entire signing procedure, but right before the signed " +
  1191  			"transaction is passed to the external caller. This method (and data) can be used by the UI to signal " +
  1192  			"to the user that the transaction was signed, but it is primarily useful for ruleset implementations." +
  1193  			"\n\n" +
  1194  			"A ruleset that implements a rate limitation needs to know what transactions are sent out to the external " +
  1195  			"interface. By hooking into this methods, the ruleset can maintain track of that count." +
  1196  			"\n\n" +
  1197  			"**OBS:** Note that if an attacker can restore your `clef` data to a previous point in time" +
  1198  			" (e.g through a backup), the attacker can reset such windows, even if he/she is unable to decrypt the content. " +
  1199  			"\n\n" +
  1200  			"The `OnApproved` method cannot be responded to, it's purely informative"
  1201  
  1202  		rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed")
  1203  		var tx types.Transaction
  1204  		rlp.DecodeBytes(rlpdata, &tx)
  1205  		add("OnApproved - SignTransactionResult", desc, &ethapi.SignTransactionResult{Raw: rlpdata, Tx: &tx})
  1206  
  1207  	}
  1208  	{ // User input
  1209  		add("UserInputRequest", "Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free)",
  1210  			&core.UserInputRequest{IsPassword: true, Title: "The title here", Prompt: "The question to ask the user"})
  1211  		add("UserInputResponse", "Response to UserInputRequest",
  1212  			&core.UserInputResponse{Text: "The textual response from user"})
  1213  	}
  1214  	{ // List request
  1215  		add("ListRequest", "Sent when a request has been made to list addresses. The UI is provided with the "+
  1216  			"full `account`s, including local directory names. Note: this information is not passed back to the external caller, "+
  1217  			"who only sees the `address`es. ",
  1218  			&core.ListRequest{
  1219  				Meta: meta,
  1220  				Accounts: []accounts.Account{
  1221  					{Address: a, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/a"}},
  1222  					{Address: b, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
  1223  			})
  1224  
  1225  		add("ListResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
  1226  			"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
  1227  			&core.ListResponse{
  1228  				Accounts: []accounts.Account{
  1229  					{
  1230  						Address: common.HexToAddress("0xcowbeef000000cowbeef00000000000000000c0w"),
  1231  						URL:     accounts.URL{Path: ".. ignored .."},
  1232  					},
  1233  					{
  1234  						Address: common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"),
  1235  					},
  1236  				}})
  1237  	}
  1238  
  1239  	fmt.Println(`## UI Client interface
  1240  
  1241  These data types are defined in the channel between clef and the UI`)
  1242  	for _, elem := range output {
  1243  		fmt.Println(elem)
  1244  	}
  1245  }
  1246  
  1247  // Quorum
  1248  // startClefAccountManagerWithPlugins - wrapped function to create a CLEF account manager with Plugin Support
  1249  func startClefAccountManagerWithPlugins(c *cli.Context, ksLocation string, nousb, lightKDF bool, scpath string) (*accounts.Manager, *plugin.PluginManager, error) {
  1250  	var err error
  1251  	// <Quorum> start the plugin manager
  1252  	var (
  1253  		pm         *plugin.PluginManager
  1254  		pluginConf *plugin.Settings
  1255  	)
  1256  	if c.IsSet(utils.PluginSettingsFlag.Name) {
  1257  		log.Info("Using plugins")
  1258  		pm, pluginConf, err = startPluginManager(c)
  1259  		if err != nil {
  1260  			utils.Fatalf(err.Error())
  1261  		}
  1262  
  1263  		// setup goroutine to stop plugin manager when clef stops
  1264  		go func() {
  1265  			sigc := make(chan os.Signal, 1)
  1266  			signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
  1267  			defer signal.Stop(sigc)
  1268  			<-sigc
  1269  			log.Info("Got interrupt, shutting down...")
  1270  			go pm.Stop()
  1271  			for i := 10; i > 0; i-- {
  1272  				<-sigc
  1273  				if i > 1 {
  1274  					log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
  1275  				}
  1276  			}
  1277  			debug.Exit() // ensure trace and CPU profile data is flushed.
  1278  			debug.LoudPanic("boom")
  1279  		}()
  1280  	}
  1281  	// </Quorum>
  1282  
  1283  	am := core.StartClefAccountManager(ksLocation, nousb, lightKDF, pluginConf, scpath)
  1284  
  1285  	// <Quorum> setup the pluggable accounts backend with the plugin
  1286  	if pm != nil && pm.IsEnabled(plugin.AccountPluginInterfaceName) {
  1287  		b := am.Backends(pluggable.BackendType)[0].(*pluggable.Backend)
  1288  		if err := pm.AddAccountPluginToBackend(b); err != nil {
  1289  			return nil, nil, err
  1290  		}
  1291  	}
  1292  	// </Quorum>
  1293  
  1294  	return am, pm, nil
  1295  }