github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/cmd/clef/README.md (about)

     1  # Clef
     2  
     3  Clef can be used to sign transactions and data and is meant as a(n eventual) replacement for Geth's account management. This allows DApps to not depend on Geth's account management. When a DApp wants to sign data (or a transaction), it can send the content to Clef, which will then provide the user with context and asks for permission to sign the content. If the users grants the signing request, Clef will send the signature back to the DApp.
     4  
     5  This setup allows a DApp to connect to a remote Ethereum node and send transactions that are locally signed. This can help in situations when a DApp is connected to an untrusted remote Ethereum node, because a local one is not available, not synchronised with the chain, or is a node that has no built-in (or limited) account management.
     6  
     7  Clef can run as a daemon on the same machine, off a usb-stick like [USB armory](https://inversepath.com/usbarmory), or even a separate VM in a [QubesOS](https://www.qubes-os.org/) type setup.
     8  
     9  Check out the
    10  
    11  * [CLI tutorial](tutorial.md) for some concrete examples on how Clef works.
    12  * [Setup docs](docs/setup.md) for infos on how to configure Clef on QubesOS or USB Armory.
    13  * [Data types](datatypes.md) for details on the communication messages between Clef and an external UI.
    14  
    15  ## Command line flags
    16  
    17  Clef accepts the following command line options:
    18  
    19  ```
    20  COMMANDS:
    21     init    Initialize the signer, generate secret storage
    22     attest  Attest that a js-file is to be used
    23     setpw   Store a credential for a keystore file
    24     delpw   Remove a credential for a keystore file
    25     gendoc  Generate documentation about json-rpc format
    26     help    Shows a list of commands or help for one command
    27  
    28  GLOBAL OPTIONS:
    29     --loglevel value        log level to emit to the screen (default: 4)
    30     --keystore value        Directory for the keystore (default: "$HOME/.ethereum/keystore")
    31     --configdir value       Directory for Clef configuration (default: "$HOME/.clef")
    32     --chainid value         Chain id to use for signing (1=mainnet, 3=Ropsten, 4=Rinkeby, 5=Goerli) (default: 1)
    33     --lightkdf              Reduce key-derivation RAM & CPU usage at some expense of KDF strength
    34     --nousb                 Disables monitoring for and managing USB hardware wallets
    35     --pcscdpath value       Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
    36     --rpcaddr value         HTTP-RPC server listening interface (default: "localhost")
    37     --rpcvhosts value       Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost")
    38     --ipcdisable            Disable the IPC-RPC server
    39     --ipcpath               Filename for IPC socket/pipe within the datadir (explicit paths escape it)
    40     --rpc                   Enable the HTTP-RPC server
    41     --rpcport value         HTTP-RPC server listening port (default: 8550)
    42     --signersecret value    A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash
    43     --4bytedb-custom value  File used for writing new 4byte-identifiers submitted via API (default: "./4byte-custom.json")
    44     --auditlog value        File used to emit audit logs. Set to "" to disable (default: "audit.log")
    45     --rules value           Path to the rule file to auto-authorize requests with
    46     --stdio-ui              Use STDIN/STDOUT as a channel for an external UI. This means that an STDIN/STDOUT is used for RPC-communication with a e.g. a graphical user interface, and can be used when Clef is started by an external process.
    47     --stdio-ui-test         Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.
    48     --advanced              If enabled, issues warnings instead of rejections for suspicious requests. Default off
    49     --help, -h              show help
    50     --version, -v           print the version
    51  ```
    52  
    53  Example:
    54  
    55  ```
    56  $ clef -keystore /my/keystore -chainid 4
    57  ```
    58  
    59  ## Security model
    60  
    61  The security model of Clef is as follows:
    62  
    63  * One critical component (the Clef binary / daemon) is responsible for handling cryptographic operations: signing, private keys, encryption/decryption of keystore files.
    64  * Clef has a well-defined 'external' API.
    65  * The 'external' API is considered UNTRUSTED.
    66  * Clef also communicates with whatever process that invoked the binary, via stdin/stdout.
    67    * This channel is considered 'trusted'. Over this channel, approvals and passwords are communicated.
    68  
    69  The general flow for signing a transaction using e.g. Geth is as follows:
    70  ![image](sign_flow.png)
    71  
    72  In this case, `geth` would be started with `--signer http://localhost:8550` and would relay requests to `eth.sendTransaction`.
    73  
    74  ## TODOs
    75  
    76  Some snags and todos
    77  
    78  * [ ] Clef should take a startup param "--no-change", for UIs that do not contain the capability to perform changes to things, only approve/deny. Such a UI should be able to start the signer in a more secure mode by telling it that it only wants approve/deny capabilities.
    79  * [x] It would be nice if Clef could collect new 4byte-id:s/method selectors, and have a secondary database for those (`4byte_custom.json`). Users could then (optionally) submit their collections for inclusion upstream.
    80  * [ ] It should be possible to configure Clef to check if an account is indeed known to it, before passing on to the UI. The reason it currently does not, is that it would make it possible to enumerate accounts if it immediately returned "unknown account" (side channel attack).
    81  * [x] It should be possible to configure Clef to auto-allow listing (certain) accounts, instead of asking every time.
    82  * [x] Done Upon startup, Clef should spit out some info to the caller (particularly important when executed in `stdio-ui`-mode), invoking methods with the following info:
    83    * [x] Version info about the signer
    84    * [x] Address of API (HTTP/IPC)
    85    * [ ] List of known accounts
    86  * [ ] Have a default timeout on signing operations, so that if the user has not answered within e.g. 60 seconds, the request is rejected.
    87  * [ ] `account_signRawTransaction`
    88  * [ ] `account_bulkSignTransactions([] transactions)` should
    89     * only exist if enabled via config/flag
    90     * only allow non-data-sending transactions
    91     * all txs must use the same `from`-account
    92     * let the user confirm, showing
    93        * the total amount
    94        * the number of unique recipients
    95  
    96  * Geth todos
    97      - The signer should pass the `Origin` header as call-info to the UI. As of right now, the way that info about the request is put together is a bit of a hack into the HTTP server. This could probably be greatly improved.
    98      - Relay: Geth should be started in `geth --signer localhost:8550`.
    99      - Currently, the Geth APIs use `common.Address` in the arguments to transaction submission (e.g `to` field). This type is 20 `bytes`, and is incapable of carrying checksum information. The signer uses `common.MixedcaseAddress`, which retains the original input.
   100      - The Geth API should switch to use the same type, and relay `to`-account verbatim to the external API.
   101  * [x] Storage
   102      * [x] An encrypted key-value storage should be implemented.
   103      * See [rules.md](rules.md) for more info about this.
   104  * Another potential thing to introduce is pairing.
   105    * To prevent spurious requests which users just accept, implement a way to "pair" the caller with the signer (external API).
   106    * Thus Geth/cpp would cryptographically handshake and afterwards the caller would be allowed to make signing requests.
   107    * This feature would make the addition of rules less dangerous.
   108  
   109  * Wallets / accounts. Add API methods for wallets.
   110  
   111  ## Communication
   112  
   113  ### External API
   114  
   115  Clef listens to HTTP requests on `rpcaddr`:`rpcport` (or to IPC on `ipcpath`), with the same JSON-RPC standard as Geth. The messages are expected to be [JSON-RPC 2.0 standard](https://www.jsonrpc.org/specification).
   116  
   117  Some of these call can require user interaction. Clients must be aware that responses may be delayed significantly or may never be received if a users decides to ignore the confirmation request.
   118  
   119  The External API is **untrusted**: it does not accept credentials over this API, nor does it expect that requests have any authority.
   120  
   121  ### Internal UI API
   122  
   123  Clef has one native console-based UI, for operation without any standalone tools. However, there is also an API to communicate with an external UI. To enable that UI, the signer needs to be executed with the `--stdio-ui` option, which allocates `stdin` / `stdout` for the UI API.
   124  
   125  An example (insecure) proof-of-concept of has been implemented in `pythonsigner.py`.
   126  
   127  The model is as follows:
   128  
   129  * The user starts the UI app (`pythonsigner.py`).
   130  * The UI app starts `clef` with `--stdio-ui`, and listens to the
   131  process output for confirmation-requests.
   132  * `clef` opens the external HTTP API.
   133  * When the `signer` receives requests, it sends a JSON-RPC request via `stdout`.
   134  * The UI app prompts the user accordingly, and responds to `clef`.
   135  * `clef` signs (or not), and responds to the original request.
   136  
   137  ## External API
   138  
   139  See the [external API changelog](extapi_changelog.md) for information about changes to this API.
   140  
   141  ### Encoding
   142  - number: positive integers that are hex encoded
   143  - data: hex encoded data
   144  - string: ASCII string
   145  
   146  All hex encoded values must be prefixed with `0x`.
   147  
   148  ## Methods
   149  
   150  ### account_new
   151  
   152  #### Create new password protected account
   153  
   154  The signer will generate a new private key, encrypts it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and stores it in the keystore directory.
   155  The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts.
   156  
   157  #### Arguments
   158  
   159  None
   160  
   161  #### Result
   162    - address [string]: account address that is derived from the generated key
   163    - url [string]: location of the keyfile
   164  
   165  #### Sample call
   166  ```json
   167  {
   168    "id": 0,
   169    "jsonrpc": "2.0",
   170    "method": "account_new",
   171    "params": []
   172  }
   173  ```
   174  Response
   175  ```
   176  {
   177    "id": 0,
   178    "jsonrpc": "2.0",
   179    "result": {
   180      "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
   181      "url": "keystore:///my/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
   182    }
   183  }
   184  ```
   185  
   186  ### account_list
   187  
   188  #### List available accounts
   189     List all accounts that this signer currently manages
   190  
   191  #### Arguments
   192  
   193  None
   194  
   195  #### Result
   196    - array with account records:
   197       - account.address [string]: account address that is derived from the generated key
   198       - account.type [string]: type of the
   199       - account.url [string]: location of the account
   200  
   201  #### Sample call
   202  ```json
   203  {
   204    "id": 1,
   205    "jsonrpc": "2.0",
   206    "method": "account_list"
   207  }
   208  ```
   209  Response
   210  ```
   211  {
   212    "id": 1,
   213    "jsonrpc": "2.0",
   214    "result": [
   215      {
   216        "address": "0xafb2f771f58513609765698f65d3f2f0224a956f",
   217        "type": "account",
   218        "url": "keystore:///tmp/keystore/UTC--2017-08-24T07-26-47.162109726Z--afb2f771f58513609765698f65d3f2f0224a956f"
   219      },
   220      {
   221        "address": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133",
   222        "type": "account",
   223        "url": "keystore:///tmp/keystore/UTC--2017-08-24T08-40-15.419655028Z--bea9183f8f4f03d427f6bcea17388bdff1cab133"
   224      }
   225    ]
   226  }
   227  ```
   228  
   229  ### account_signTransaction
   230  
   231  #### Sign transactions
   232     Signs a transactions and responds with the signed transaction in RLP encoded form.
   233  
   234  #### Arguments
   235    2. transaction object:
   236       - `from` [address]: account to send the transaction from
   237       - `to` [address]: receiver account. If omitted or `0x`, will cause contract creation.
   238       - `gas` [number]: maximum amount of gas to burn
   239       - `gasPrice` [number]: gas price
   240       - `value` [number:optional]: amount of Wei to send with the transaction
   241       - `data` [data:optional]:  input data
   242       - `nonce` [number]: account nonce
   243    3. method signature [string:optional]
   244       - The method signature, if present, is to aid decoding the calldata. Should consist of `methodname(paramtype,...)`, e.g. `transfer(uint256,address)`. The signer may use this data to parse the supplied calldata, and show the user. The data, however, is considered totally untrusted, and reliability is not expected.
   245  
   246  
   247  #### Result
   248    - signed transaction in RLP encoded form [data]
   249  
   250  #### Sample call
   251  ```json
   252  {
   253    "id": 2,
   254    "jsonrpc": "2.0",
   255    "method": "account_signTransaction",
   256    "params": [
   257      {
   258        "from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
   259        "gas": "0x55555",
   260        "gasPrice": "0x1234",
   261        "input": "0xabcd",
   262        "nonce": "0x0",
   263        "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
   264        "value": "0x1234"
   265      }
   266    ]
   267  }
   268  ```
   269  Response
   270  
   271  ```json
   272  {
   273    "id": 2,
   274    "jsonrpc": "2.0",
   275    "error": {
   276      "code": -32000,
   277      "message": "Request denied"
   278    }
   279  }
   280  ```
   281  #### Sample call with ABI-data
   282  
   283  
   284  ```json
   285  {
   286    "id": 67,
   287    "jsonrpc": "2.0",
   288    "method": "account_signTransaction",
   289    "params": [
   290      {
   291        "from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa",
   292        "gas": "0x333",
   293        "gasPrice": "0x1",
   294        "nonce": "0x0",
   295        "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
   296        "value": "0x0",
   297        "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"
   298      },
   299      "safeSend(address)"
   300    ]
   301  }
   302  ```
   303  Response
   304  
   305  ```json
   306  {
   307    "jsonrpc": "2.0",
   308    "id": 67,
   309    "result": {
   310      "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
   311      "tx": {
   312        "nonce": "0x0",
   313        "gasPrice": "0x1",
   314        "gas": "0x333",
   315        "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
   316        "value": "0x0",
   317        "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
   318        "v": "0x26",
   319        "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
   320        "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
   321        "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
   322      }
   323    }
   324  }
   325  ```
   326  
   327  Bash example:
   328  ```bash
   329  #curl -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
   330  
   331  {"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}}
   332  ```
   333  
   334  ### account_signData
   335  
   336  #### Sign data
   337     Signs a chunk of data and returns the calculated signature.
   338  
   339  #### Arguments
   340    - content type [string]: type of signed data
   341       - `text/validator`: hex data with custom validator defined in a contract
   342       - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers
   343       - `text/plain`: simple hex data validated by `account_ecRecover`
   344    - account [address]: account to sign with
   345    - data [object]: data to sign
   346  
   347  #### Result
   348    - calculated signature [data]
   349  
   350  #### Sample call
   351  ```json
   352  {
   353    "id": 3,
   354    "jsonrpc": "2.0",
   355    "method": "account_signData",
   356    "params": [
   357      "data/plain",
   358      "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
   359      "0xaabbccdd"
   360    ]
   361  }
   362  ```
   363  Response
   364  
   365  ```json
   366  {
   367    "id": 3,
   368    "jsonrpc": "2.0",
   369    "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
   370  }
   371  ```
   372  
   373  ### account_signTypedData
   374  
   375  #### Sign data
   376     Signs a chunk of structured data conformant to [EIP712]([EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md)) and returns the calculated signature.
   377  
   378  #### Arguments
   379    - account [address]: account to sign with
   380    - data [object]: data to sign
   381  
   382  #### Result
   383    - calculated signature [data]
   384  
   385  #### Sample call
   386  ```json
   387  {
   388    "id": 68,
   389    "jsonrpc": "2.0",
   390    "method": "account_signTypedData",
   391    "params": [
   392      "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826",
   393      {
   394        "types": {
   395          "EIP712Domain": [
   396            {
   397              "name": "name",
   398              "type": "string"
   399            },
   400            {
   401              "name": "version",
   402              "type": "string"
   403            },
   404            {
   405              "name": "chainId",
   406              "type": "uint256"
   407            },
   408            {
   409              "name": "verifyingContract",
   410              "type": "address"
   411            }
   412          ],
   413          "Person": [
   414            {
   415              "name": "name",
   416              "type": "string"
   417            },
   418            {
   419              "name": "wallet",
   420              "type": "address"
   421            }
   422          ],
   423          "Mail": [
   424            {
   425              "name": "from",
   426              "type": "Person"
   427            },
   428            {
   429              "name": "to",
   430              "type": "Person"
   431            },
   432            {
   433              "name": "contents",
   434              "type": "string"
   435            }
   436          ]
   437        },
   438        "primaryType": "Mail",
   439        "domain": {
   440          "name": "Ether Mail",
   441          "version": "1",
   442          "chainId": 1,
   443          "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
   444        },
   445        "message": {
   446          "from": {
   447            "name": "Cow",
   448            "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
   449          },
   450          "to": {
   451            "name": "Bob",
   452            "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
   453          },
   454          "contents": "Hello, Bob!"
   455        }
   456      }
   457    ]
   458  }
   459  ```
   460  Response
   461  
   462  ```json
   463  {
   464      "id": 1,
   465      "jsonrpc": "2.0",
   466      "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c"
   467  }
   468  ```
   469  
   470  ### account_ecRecover
   471  
   472  #### Sign data
   473  
   474  Derive the address from the account that was used to sign data with content type `text/plain` and the signature.
   475  
   476  #### Arguments
   477    - data [data]: data that was signed
   478    - signature [data]: the signature to verify
   479  
   480  #### Result
   481    - derived account [address]
   482  
   483  #### Sample call
   484  ```json
   485  {
   486    "id": 4,
   487    "jsonrpc": "2.0",
   488    "method": "account_ecRecover",
   489    "params": [
   490      "data/plain",
   491      "0xaabbccdd",
   492      "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
   493    ]
   494  }
   495  ```
   496  Response
   497  
   498  ```json
   499  {
   500    "id": 4,
   501    "jsonrpc": "2.0",
   502    "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db"
   503  }
   504  ```
   505  
   506  ### account_import
   507  
   508  #### Import account
   509     Import a private key into the keystore. The imported key is expected to be encrypted according to the web3 keystore
   510     format.
   511  
   512  #### Arguments
   513    - account [object]: key in [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) (retrieved with account_export)
   514  
   515  #### Result
   516    - imported key [object]:
   517       - key.address [address]: address of the imported key
   518       - key.type [string]: type of the account
   519       - key.url [string]: key URL
   520  
   521  #### Sample call
   522  ```json
   523  {
   524    "id": 6,
   525    "jsonrpc": "2.0",
   526    "method": "account_import",
   527    "params": [
   528      {
   529        "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
   530        "crypto": {
   531          "cipher": "aes-128-ctr",
   532          "cipherparams": {
   533            "iv": "401c39a7c7af0388491c3d3ecb39f532"
   534          },
   535          "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
   536          "kdf": "scrypt",
   537          "kdfparams": {
   538            "dklen": 32,
   539            "n": 262144,
   540            "p": 1,
   541            "r": 8,
   542            "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
   543          },
   544          "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
   545        },
   546        "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
   547        "version": 3
   548      }
   549    ]
   550  }
   551  ```
   552  Response
   553  
   554  ```json
   555  {
   556    "id": 6,
   557    "jsonrpc": "2.0",
   558    "result": {
   559      "address": "0xc7412fc59930fd90099c917a50e5f11d0934b2f5",
   560      "type": "account",
   561      "url": "keystore:///tmp/keystore/UTC--2017-08-24T11-00-42.032024108Z--c7412fc59930fd90099c917a50e5f11d0934b2f5"
   562    }
   563  }
   564  ```
   565  
   566  ### account_export
   567  
   568  #### Export account from keystore
   569     Export a private key from the keystore. The exported private key is encrypted with the original password. When the
   570     key is imported later this password is required.
   571  
   572  #### Arguments
   573    - account [address]: export private key that is associated with this account
   574  
   575  #### Result
   576    - exported key, see [web3 keystore format](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) for
   577    more information
   578  
   579  #### Sample call
   580  ```json
   581  {
   582    "id": 5,
   583    "jsonrpc": "2.0",
   584    "method": "account_export",
   585    "params": [
   586      "0xc7412fc59930fd90099c917a50e5f11d0934b2f5"
   587    ]
   588  }
   589  ```
   590  Response
   591  
   592  ```json
   593  {
   594    "id": 5,
   595    "jsonrpc": "2.0",
   596    "result": {
   597      "address": "c7412fc59930fd90099c917a50e5f11d0934b2f5",
   598      "crypto": {
   599        "cipher": "aes-128-ctr",
   600        "cipherparams": {
   601          "iv": "401c39a7c7af0388491c3d3ecb39f532"
   602        },
   603        "ciphertext": "eb045260b18dd35cd0e6d99ead52f8fa1e63a6b0af2d52a8de198e59ad783204",
   604        "kdf": "scrypt",
   605        "kdfparams": {
   606          "dklen": 32,
   607          "n": 262144,
   608          "p": 1,
   609          "r": 8,
   610          "salt": "9a657e3618527c9b5580ded60c12092e5038922667b7b76b906496f021bb841a"
   611        },
   612        "mac": "880dc10bc06e9cec78eb9830aeb1e7a4a26b4c2c19615c94acb632992b952806"
   613      },
   614      "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
   615      "version": 3
   616    }
   617  }
   618  ```
   619  
   620  ## UI API
   621  
   622  These methods needs to be implemented by a UI listener.
   623  
   624  By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with
   625  denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
   626  See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'.
   627  
   628  All methods in this API uses object-based parameters, so that there can be no mixups of parameters: each piece of data is accessed by key.
   629  
   630  See the [ui API changelog](intapi_changelog.md) for information about changes to this API.
   631  
   632  OBS! A slight deviation from `json` standard is in place: every request and response should be confined to a single line.
   633  Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make
   634  things simpler for both parties.
   635  
   636  ### ApproveTx / `ui_approveTx`
   637  
   638  Invoked when there's a transaction for approval.
   639  
   640  
   641  #### Sample call
   642  
   643  Here's a method invocation:
   644  ```bash
   645  
   646  curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
   647  ```
   648  Results in the following invocation on the UI:
   649  ```json
   650  
   651  {
   652    "jsonrpc": "2.0",
   653    "id": 1,
   654    "method": "ui_approveTx",
   655    "params": [
   656      {
   657        "transaction": {
   658          "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa",
   659          "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
   660          "gas": "0x333",
   661          "gasPrice": "0x1",
   662          "value": "0x0",
   663          "nonce": "0x0",
   664          "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
   665          "input": null
   666        },
   667        "call_info": [
   668            {
   669              "type": "WARNING",
   670              "message": "Invalid checksum on to-address"
   671            },
   672            {
   673              "type": "Info",
   674              "message": "safeSend(address: 0x0000000000000000000000000000000000000012)"
   675            }
   676          ],
   677        "meta": {
   678          "remote": "127.0.0.1:48486",
   679          "local": "localhost:8550",
   680          "scheme": "HTTP/1.1"
   681        }
   682      }
   683    ]
   684  }
   685  
   686  ```
   687  
   688  The same method invocation, but with invalid data:
   689  ```bash
   690  
   691  curl -i -H "Content-Type: application/json" -X POST --data '{"jsonrpc":"2.0","method":"account_signTransaction","params":[{"from":"0x694267f14675d7e1b9494fd8d72fefe1755710fa","gas":"0x333","gasPrice":"0x1","nonce":"0x0","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0", "value":"0x0", "data":"0x4401a6e40000000000000002000000000000000000000000000000000000000000000012"},"safeSend(address)"],"id":67}' http://localhost:8550/
   692  ```
   693  
   694  ```json
   695  
   696  {
   697    "jsonrpc": "2.0",
   698    "id": 1,
   699    "method": "ui_approveTx",
   700    "params": [
   701      {
   702        "transaction": {
   703          "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa",
   704          "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
   705          "gas": "0x333",
   706          "gasPrice": "0x1",
   707          "value": "0x0",
   708          "nonce": "0x0",
   709          "data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012",
   710          "input": null
   711        },
   712        "call_info": [
   713            {
   714              "type": "WARNING",
   715              "message": "Invalid checksum on to-address"
   716            },
   717            {
   718              "type": "WARNING",
   719              "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)"
   720            }
   721          ],
   722        "meta": {
   723          "remote": "127.0.0.1:48492",
   724          "local": "localhost:8550",
   725          "scheme": "HTTP/1.1"
   726        }
   727      }
   728    ]
   729  }
   730  
   731  
   732  ```
   733  
   734  One which has missing `to`, but with no `data`:
   735  
   736  
   737  ```json
   738  
   739  {
   740    "jsonrpc": "2.0",
   741    "id": 3,
   742    "method": "ui_approveTx",
   743    "params": [
   744      {
   745        "transaction": {
   746          "from": "",
   747          "to": null,
   748          "gas": "0x0",
   749          "gasPrice": "0x0",
   750          "value": "0x0",
   751          "nonce": "0x0",
   752          "data": null,
   753          "input": null
   754        },
   755        "call_info": [
   756            {
   757              "type": "CRITICAL",
   758              "message": "Tx will create contract with empty code!"
   759            }
   760          ],
   761        "meta": {
   762          "remote": "signer binary",
   763          "local": "main",
   764          "scheme": "in-proc"
   765        }
   766      }
   767    ]
   768  }
   769  ```
   770  
   771  ### ApproveListing / `ui_approveListing`
   772  
   773  Invoked when a request for account listing has been made.
   774  
   775  #### Sample call
   776  
   777  ```json
   778  
   779  {
   780    "jsonrpc": "2.0",
   781    "id": 5,
   782    "method": "ui_approveListing",
   783    "params": [
   784      {
   785        "accounts": [
   786          {
   787            "type": "Account",
   788            "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42",
   789            "address": "0x123409812340981234098123409812deadbeef42"
   790          },
   791          {
   792            "type": "Account",
   793            "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42",
   794            "address": "0xcafebabedeadbeef34098123409812deadbeef42"
   795          }
   796        ],
   797        "meta": {
   798          "remote": "signer binary",
   799          "local": "main",
   800          "scheme": "in-proc"
   801        }
   802      }
   803    ]
   804  }
   805  
   806  ```
   807  
   808  
   809  ### ApproveSignData / `ui_approveSignData`
   810  
   811  #### Sample call
   812  
   813  ```json
   814  {
   815    "jsonrpc": "2.0",
   816    "id": 4,
   817    "method": "ui_approveSignData",
   818    "params": [
   819      {
   820        "address": "0x123409812340981234098123409812deadbeef42",
   821        "raw_data": "0x01020304",
   822        "message": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004",
   823        "hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310",
   824        "meta": {
   825          "remote": "signer binary",
   826          "local": "main",
   827          "scheme": "in-proc"
   828        }
   829      }
   830    ]
   831  }
   832  
   833  ```
   834  
   835  ### ShowInfo / `ui_showInfo`
   836  
   837  The UI should show the info to the user. Does not expect response.
   838  
   839  #### Sample call
   840  
   841  ```json
   842  {
   843    "jsonrpc": "2.0",
   844    "id": 9,
   845    "method": "ui_showInfo",
   846    "params": [
   847      {
   848        "text": "Tests completed"
   849      }
   850    ]
   851  }
   852  
   853  ```
   854  
   855  ### ShowError / `ui_showError`
   856  
   857  The UI should show the info to the user. Does not expect response.
   858  
   859  ```json
   860  
   861  {
   862    "jsonrpc": "2.0",
   863    "id": 2,
   864    "method": "ShowError",
   865    "params": [
   866      {
   867        "text": "Testing 'ShowError'"
   868      }
   869    ]
   870  }
   871  
   872  ```
   873  
   874  ### OnApprovedTx / `ui_onApprovedTx`
   875  
   876  `OnApprovedTx` is called when a transaction has been approved and signed. The call contains the return value that will be sent to the external caller.  The return value from this method is ignored - the reason for having this callback is to allow the ruleset to keep track of approved transactions.
   877  
   878  When implementing rate-limited rules, this callback should be used.
   879  
   880  TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
   881  
   882  ### OnSignerStartup / `ui_onSignerStartup`
   883  
   884  This method provide the UI with information about what API version the signer uses (both internal and external) aswell as build-info and external API,
   885  in k/v-form.
   886  
   887  Example call:
   888  ```json
   889  
   890  {
   891    "jsonrpc": "2.0",
   892    "id": 1,
   893    "method": "ui_onSignerStartup",
   894    "params": [
   895      {
   896        "info": {
   897          "extapi_http": "http://localhost:8550",
   898          "extapi_ipc": null,
   899          "extapi_version": "2.0.0",
   900          "intapi_version": "1.2.0"
   901        }
   902      }
   903    ]
   904  }
   905  
   906  ```
   907  
   908  
   909  ### Rules for UI apis
   910  
   911  A UI should conform to the following rules.
   912  
   913  * A UI MUST NOT load any external resources that were not embedded/part of the UI package.
   914    * For example, not load icons, stylesheets from the internet
   915    * Not load files from the filesystem, unless they reside in the same local directory (e.g. config files)
   916  * A Graphical UI MUST show the blocky-identicon for ethereum addresses.
   917  * A UI MUST warn display appropriate warning if the destination-account is formatted with invalid checksum.
   918  * A UI MUST NOT open any ports or services
   919    * The signer opens the public port
   920  * A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write.
   921  * A UI SHOULD inform the user about the `SHA256` or `MD5` hash of the binary being executed
   922  * A UI SHOULD NOT maintain a secondary storage of data, e.g. list of accounts
   923    * The signer provides accounts
   924  * A UI SHOULD, to the best extent possible, use static linking / bundling, so that required libraries are bundled
   925  along with the UI.
   926  
   927  
   928  ### UI Implementations
   929  
   930  There are a couple of implementation for a UI. We'll try to keep this list up to date.
   931  
   932  | Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters|
   933  | ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- |
   934  | QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: |  :+1: (partially)|
   935  | GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: |  :x: |
   936  | Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: |  :x: |
   937  | Clef UI| https://github.com/kyokan/clef-ui| Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: |  :+1: (approve tx only)|