github.com/core-coin/go-core/v2@v2.1.9/cmd/clef/main.go (about)

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