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