• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..23-Dec-2021-

docs/H03-May-2022-240153

testdata/H23-Dec-2021-8883

tests/H03-May-2022-

README.mdH A D23-Dec-202130.1 KiB923763

datatypes.mdH A D23-Dec-20216.2 KiB225195

extapi_changelog.mdH A D23-Dec-20213.7 KiB10582

intapi_changelog.mdH A D23-Dec-20216.1 KiB192147

main.goH A D23-Dec-202139 KiB1,1511,007

pythonsigner.pyH A D23-Dec-20215.1 KiB178140

rules.mdH A D23-Dec-20219 KiB235172

tutorial.mdH A D23-Dec-202116 KiB354265

README.md

1# Clef
2
3Clef 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
5This 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
7Clef 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
9Check out the
10
11* [CLI tutorial](tutorial.md) for some concrete examples on how Clef works.
12* [Setup docs](docs/setup.md) for information 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
17Clef accepts the following command line options:
18
19```
20COMMANDS:
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
28GLOBAL 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   --http.addr value       HTTP-RPC server listening interface (default: "localhost")
37   --http.vhosts 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   --http                  Enable the HTTP-RPC server
41   --http.port 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   --suppress-bootwarn     If set, does not show the warning during boot
50   --help, -h              show help
51   --version, -v           print the version
52```
53
54Example:
55
56```
57$ clef -keystore /my/keystore -chainid 4
58```
59
60## Security model
61
62The security model of Clef is as follows:
63
64* One critical component (the Clef binary / daemon) is responsible for handling cryptographic operations: signing, private keys, encryption/decryption of keystore files.
65* Clef has a well-defined 'external' API.
66* The 'external' API is considered UNTRUSTED.
67* Clef also communicates with whatever process that invoked the binary, via stdin/stdout.
68  * This channel is considered 'trusted'. Over this channel, approvals and passwords are communicated.
69
70The general flow for signing a transaction using e.g. Geth is as follows:
71![image](sign_flow.png)
72
73In this case, `geth` would be started with `--signer http://localhost:8550` and would relay requests to `eth.sendTransaction`.
74
75## TODOs
76
77Some snags and todos
78
79* [ ] 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.
80* [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.
81* [ ] 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).
82* [x] It should be possible to configure Clef to auto-allow listing (certain) accounts, instead of asking every time.
83* [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:
84  * [x] Version info about the signer
85  * [x] Address of API (HTTP/IPC)
86  * [ ] List of known accounts
87* [ ] Have a default timeout on signing operations, so that if the user has not answered within e.g. 60 seconds, the request is rejected.
88* [ ] `account_signRawTransaction`
89* [ ] `account_bulkSignTransactions([] transactions)` should
90   * only exist if enabled via config/flag
91   * only allow non-data-sending transactions
92   * all txs must use the same `from`-account
93   * let the user confirm, showing
94      * the total amount
95      * the number of unique recipients
96
97* Geth todos
98    - 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.
99    - Relay: Geth should be started in `geth --signer localhost:8550`.
100    - 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.
101    - The Geth API should switch to use the same type, and relay `to`-account verbatim to the external API.
102* [x] Storage
103    * [x] An encrypted key-value storage should be implemented.
104    * See [rules.md](rules.md) for more info about this.
105* Another potential thing to introduce is pairing.
106  * To prevent spurious requests which users just accept, implement a way to "pair" the caller with the signer (external API).
107  * Thus Geth/cpp would cryptographically handshake and afterwards the caller would be allowed to make signing requests.
108  * This feature would make the addition of rules less dangerous.
109
110* Wallets / accounts. Add API methods for wallets.
111
112## Communication
113
114### External API
115
116Clef listens to HTTP requests on `http.addr`:`http.port` (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).
117
118Some of these calls can require user interaction. Clients must be aware that responses may be delayed significantly or may never be received if a user decides to ignore the confirmation request.
119
120The External API is **untrusted**: it does not accept credentials, nor does it expect that requests have any authority.
121
122### Internal UI API
123
124Clef 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.
125
126An example (insecure) proof-of-concept of has been implemented in `pythonsigner.py`.
127
128The model is as follows:
129
130* The user starts the UI app (`pythonsigner.py`).
131* The UI app starts `clef` with `--stdio-ui`, and listens to the
132process output for confirmation-requests.
133* `clef` opens the external HTTP API.
134* When the `signer` receives requests, it sends a JSON-RPC request via `stdout`.
135* The UI app prompts the user accordingly, and responds to `clef`.
136* `clef` signs (or not), and responds to the original request.
137
138## External API
139
140See the [external API changelog](extapi_changelog.md) for information about changes to this API.
141
142### Encoding
143- number: positive integers that are hex encoded
144- data: hex encoded data
145- string: ASCII string
146
147All hex encoded values must be prefixed with `0x`.
148
149### account_new
150
151#### Create new password protected account
152
153The signer will generate a new private key, encrypt it according to [web3 keystore spec](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) and store it in the keystore directory.
154The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts.
155
156#### Arguments
157
158None
159
160#### Result
161  - address [string]: account address that is derived from the generated key
162
163#### Sample call
164```json
165{
166  "id": 0,
167  "jsonrpc": "2.0",
168  "method": "account_new",
169  "params": []
170}
171```
172Response
173```json
174{
175  "id": 0,
176  "jsonrpc": "2.0",
177  "result": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133"
178}
179```
180
181### account_list
182
183#### List available accounts
184   List all accounts that this signer currently manages
185
186#### Arguments
187
188None
189
190#### Result
191  - array with account records:
192     - account.address [string]: account address that is derived from the generated key
193
194#### Sample call
195```json
196{
197  "id": 1,
198  "jsonrpc": "2.0",
199  "method": "account_list"
200}
201```
202Response
203```json
204{
205  "id": 1,
206  "jsonrpc": "2.0",
207  "result": [
208    "0xafb2f771f58513609765698f65d3f2f0224a956f",
209    "0xbea9183f8f4f03d427f6bcea17388bdff1cab133"
210  ]
211}
212```
213
214### account_signTransaction
215
216#### Sign transactions
217   Signs a transaction and responds with the signed transaction in RLP-encoded and JSON forms.
218
219#### Arguments
220  1. transaction object:
221     - `from` [address]: account to send the transaction from
222     - `to` [address]: receiver account. If omitted or `0x`, will cause contract creation.
223     - `gas` [number]: maximum amount of gas to burn
224     - `gasPrice` [number]: gas price
225     - `value` [number:optional]: amount of Wei to send with the transaction
226     - `data` [data:optional]:  input data
227     - `nonce` [number]: account nonce
228  1. method signature [string:optional]
229     - 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.
230
231
232#### Result
233  - raw [data]: signed transaction in RLP encoded form
234  - tx [json]: signed transaction in JSON form
235
236#### Sample call
237```json
238{
239  "id": 2,
240  "jsonrpc": "2.0",
241  "method": "account_signTransaction",
242  "params": [
243    {
244      "from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
245      "gas": "0x55555",
246      "gasPrice": "0x1234",
247      "input": "0xabcd",
248      "nonce": "0x0",
249      "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
250      "value": "0x1234"
251    }
252  ]
253}
254```
255Response
256
257```json
258{
259  "jsonrpc": "2.0",
260  "id": 2,
261  "result": {
262    "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
263    "tx": {
264      "nonce": "0x0",
265      "gasPrice": "0x1234",
266      "gas": "0x55555",
267      "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
268      "value": "0x1234",
269      "input": "0xabcd",
270      "v": "0x26",
271      "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
272      "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
273      "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
274    }
275  }
276}
277```
278#### Sample call with ABI-data
279
280
281```json
282{
283  "id": 67,
284  "jsonrpc": "2.0",
285  "method": "account_signTransaction",
286  "params": [
287    {
288      "from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa",
289      "gas": "0x333",
290      "gasPrice": "0x1",
291      "nonce": "0x0",
292      "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
293      "value": "0x0",
294      "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"
295    },
296    "safeSend(address)"
297  ]
298}
299```
300Response
301
302```json
303{
304  "jsonrpc": "2.0",
305  "id": 67,
306  "result": {
307    "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
308    "tx": {
309      "nonce": "0x0",
310      "gasPrice": "0x1",
311      "gas": "0x333",
312      "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
313      "value": "0x0",
314      "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
315      "v": "0x26",
316      "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
317      "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
318      "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
319    }
320  }
321}
322```
323
324Bash example:
325```bash
326> 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/
327
328{"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"}}}
329```
330
331### account_signData
332
333#### Sign data
334   Signs a chunk of data and returns the calculated signature.
335
336#### Arguments
337  - content type [string]: type of signed data
338     - `text/validator`: hex data with custom validator defined in a contract
339     - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers
340     - `text/plain`: simple hex data validated by `account_ecRecover`
341  - account [address]: account to sign with
342  - data [object]: data to sign
343
344#### Result
345  - calculated signature [data]
346
347#### Sample call
348```json
349{
350  "id": 3,
351  "jsonrpc": "2.0",
352  "method": "account_signData",
353  "params": [
354    "data/plain",
355    "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
356    "0xaabbccdd"
357  ]
358}
359```
360Response
361
362```json
363{
364  "id": 3,
365  "jsonrpc": "2.0",
366  "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
367}
368```
369
370### account_signTypedData
371
372#### Sign data
373   Signs a chunk of structured data conformant to [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and returns the calculated signature.
374
375#### Arguments
376  - account [address]: account to sign with
377  - data [object]: data to sign
378
379#### Result
380  - calculated signature [data]
381
382#### Sample call
383```json
384{
385  "id": 68,
386  "jsonrpc": "2.0",
387  "method": "account_signTypedData",
388  "params": [
389    "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826",
390    {
391      "types": {
392        "EIP712Domain": [
393          {
394            "name": "name",
395            "type": "string"
396          },
397          {
398            "name": "version",
399            "type": "string"
400          },
401          {
402            "name": "chainId",
403            "type": "uint256"
404          },
405          {
406            "name": "verifyingContract",
407            "type": "address"
408          }
409        ],
410        "Person": [
411          {
412            "name": "name",
413            "type": "string"
414          },
415          {
416            "name": "wallet",
417            "type": "address"
418          }
419        ],
420        "Mail": [
421          {
422            "name": "from",
423            "type": "Person"
424          },
425          {
426            "name": "to",
427            "type": "Person"
428          },
429          {
430            "name": "contents",
431            "type": "string"
432          }
433        ]
434      },
435      "primaryType": "Mail",
436      "domain": {
437        "name": "Ether Mail",
438        "version": "1",
439        "chainId": 1,
440        "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
441      },
442      "message": {
443        "from": {
444          "name": "Cow",
445          "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
446        },
447        "to": {
448          "name": "Bob",
449          "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
450        },
451        "contents": "Hello, Bob!"
452      }
453    }
454  ]
455}
456```
457Response
458
459```json
460{
461    "id": 1,
462    "jsonrpc": "2.0",
463    "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c"
464}
465```
466
467### account_ecRecover
468
469#### Recover the signing address
470
471Derive the address from the account that was used to sign data with content type `text/plain` and the signature.
472
473#### Arguments
474  - data [data]: data that was signed
475  - signature [data]: the signature to verify
476
477#### Result
478  - derived account [address]
479
480#### Sample call
481```json
482{
483  "id": 4,
484  "jsonrpc": "2.0",
485  "method": "account_ecRecover",
486  "params": [
487    "0xaabbccdd",
488    "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
489  ]
490}
491```
492Response
493
494```json
495{
496  "id": 4,
497  "jsonrpc": "2.0",
498  "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db"
499}
500```
501
502### account_version
503
504#### Get external API version
505
506Get the version of the external API used by Clef.
507
508#### Arguments
509
510None
511
512#### Result
513
514* external API version [string]
515
516#### Sample call
517```json
518{
519  "id": 0,
520  "jsonrpc": "2.0",
521  "method": "account_version",
522  "params": []
523}
524```
525
526Response
527```json
528{
529    "id": 0,
530    "jsonrpc": "2.0",
531    "result": "6.0.0"
532}
533```
534
535## UI API
536
537These methods needs to be implemented by a UI listener.
538
539By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with
540denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented.
541See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'.
542
543All methods in this API use object-based parameters, so that there can be no mixup of parameters: each piece of data is accessed by key.
544
545See the [ui API changelog](intapi_changelog.md) for information about changes to this API.
546
547OBS! A slight deviation from `json` standard is in place: every request and response should be confined to a single line.
548Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make
549things simpler for both parties.
550
551### ApproveTx / `ui_approveTx`
552
553Invoked when there's a transaction for approval.
554
555
556#### Sample call
557
558Here's a method invocation:
559```bash
560
561curl -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/
562```
563Results in the following invocation on the UI:
564```json
565
566{
567  "jsonrpc": "2.0",
568  "id": 1,
569  "method": "ui_approveTx",
570  "params": [
571    {
572      "transaction": {
573        "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa",
574        "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
575        "gas": "0x333",
576        "gasPrice": "0x1",
577        "value": "0x0",
578        "nonce": "0x0",
579        "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
580        "input": null
581      },
582      "call_info": [
583          {
584            "type": "WARNING",
585            "message": "Invalid checksum on to-address"
586          },
587          {
588            "type": "Info",
589            "message": "safeSend(address: 0x0000000000000000000000000000000000000012)"
590          }
591        ],
592      "meta": {
593        "remote": "127.0.0.1:48486",
594        "local": "localhost:8550",
595        "scheme": "HTTP/1.1"
596      }
597    }
598  ]
599}
600
601```
602
603The same method invocation, but with invalid data:
604```bash
605
606curl -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/
607```
608
609```json
610
611{
612  "jsonrpc": "2.0",
613  "id": 1,
614  "method": "ui_approveTx",
615  "params": [
616    {
617      "transaction": {
618        "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa",
619        "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
620        "gas": "0x333",
621        "gasPrice": "0x1",
622        "value": "0x0",
623        "nonce": "0x0",
624        "data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012",
625        "input": null
626      },
627      "call_info": [
628          {
629            "type": "WARNING",
630            "message": "Invalid checksum on to-address"
631          },
632          {
633            "type": "WARNING",
634            "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)"
635          }
636        ],
637      "meta": {
638        "remote": "127.0.0.1:48492",
639        "local": "localhost:8550",
640        "scheme": "HTTP/1.1"
641      }
642    }
643  ]
644}
645
646
647```
648
649One which has missing `to`, but with no `data`:
650
651
652```json
653
654{
655  "jsonrpc": "2.0",
656  "id": 3,
657  "method": "ui_approveTx",
658  "params": [
659    {
660      "transaction": {
661        "from": "",
662        "to": null,
663        "gas": "0x0",
664        "gasPrice": "0x0",
665        "value": "0x0",
666        "nonce": "0x0",
667        "data": null,
668        "input": null
669      },
670      "call_info": [
671          {
672            "type": "CRITICAL",
673            "message": "Tx will create contract with empty code!"
674          }
675        ],
676      "meta": {
677        "remote": "signer binary",
678        "local": "main",
679        "scheme": "in-proc"
680      }
681    }
682  ]
683}
684```
685
686### ApproveListing / `ui_approveListing`
687
688Invoked when a request for account listing has been made.
689
690#### Sample call
691
692```json
693
694{
695  "jsonrpc": "2.0",
696  "id": 5,
697  "method": "ui_approveListing",
698  "params": [
699    {
700      "accounts": [
701        {
702          "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42",
703          "address": "0x123409812340981234098123409812deadbeef42"
704        },
705        {
706          "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42",
707          "address": "0xcafebabedeadbeef34098123409812deadbeef42"
708        }
709      ],
710      "meta": {
711        "remote": "signer binary",
712        "local": "main",
713        "scheme": "in-proc"
714      }
715    }
716  ]
717}
718
719```
720
721
722### ApproveSignData / `ui_approveSignData`
723
724#### Sample call
725
726```json
727{
728  "jsonrpc": "2.0",
729  "id": 4,
730  "method": "ui_approveSignData",
731  "params": [
732    {
733      "address": "0x123409812340981234098123409812deadbeef42",
734      "raw_data": "0x01020304",
735      "messages": [
736        {
737          "name": "message",
738          "value": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004",
739          "type": "text/plain"
740        }
741      ],
742      "hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310",
743      "meta": {
744        "remote": "signer binary",
745        "local": "main",
746        "scheme": "in-proc"
747      }
748    }
749  ]
750}
751```
752
753### ApproveNewAccount / `ui_approveNewAccount`
754
755Invoked when a request for creating a new account has been made.
756
757#### Sample call
758
759```json
760{
761  "jsonrpc": "2.0",
762  "id": 4,
763  "method": "ui_approveNewAccount",
764  "params": [
765    {
766      "meta": {
767        "remote": "signer binary",
768        "local": "main",
769        "scheme": "in-proc"
770      }
771    }
772  ]
773}
774```
775
776### ShowInfo / `ui_showInfo`
777
778The UI should show the info (a single message) to the user. Does not expect response.
779
780#### Sample call
781
782```json
783{
784  "jsonrpc": "2.0",
785  "id": 9,
786  "method": "ui_showInfo",
787  "params": [
788    "Tests completed"
789  ]
790}
791
792```
793
794### ShowError / `ui_showError`
795
796The UI should show the error (a single message) to the user. Does not expect response.
797
798```json
799
800{
801  "jsonrpc": "2.0",
802  "id": 2,
803  "method": "ui_showError",
804  "params": [
805    "Something bad happened!"
806  ]
807}
808
809```
810
811### OnApprovedTx / `ui_onApprovedTx`
812
813`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.
814
815When implementing rate-limited rules, this callback should be used.
816
817TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`.
818
819Example call:
820```json
821
822{
823  "jsonrpc": "2.0",
824  "id": 1,
825  "method": "ui_onApprovedTx",
826  "params": [
827    {
828      "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
829      "tx": {
830        "nonce": "0x0",
831        "gasPrice": "0x1",
832        "gas": "0x333",
833        "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0",
834        "value": "0x0",
835        "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012",
836        "v": "0x26",
837        "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e",
838        "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663",
839        "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"
840      }
841    }
842  ]
843}
844```
845
846### OnSignerStartup / `ui_onSignerStartup`
847
848This method provides the UI with information about what API version the signer uses (both internal and external) as well as build-info and external API,
849in k/v-form.
850
851Example call:
852```json
853
854{
855  "jsonrpc": "2.0",
856  "id": 1,
857  "method": "ui_onSignerStartup",
858  "params": [
859    {
860      "info": {
861        "extapi_http": "http://localhost:8550",
862        "extapi_ipc": null,
863        "extapi_version": "2.0.0",
864        "intapi_version": "1.2.0"
865      }
866    }
867  ]
868}
869
870```
871
872### OnInputRequired / `ui_onInputRequired`
873
874Invoked when Clef requires user input (e.g. a password).
875
876Example call:
877```json
878
879{
880  "jsonrpc": "2.0",
881  "id": 1,
882  "method": "ui_onInputRequired",
883  "params": [
884    {
885      "title": "Account password",
886      "prompt": "Please enter the password for account 0x694267f14675d7e1b9494fd8d72fefe1755710fa",
887      "isPassword": true
888    }
889  ]
890}
891```
892
893
894### Rules for UI apis
895
896A UI should conform to the following rules.
897
898* A UI MUST NOT load any external resources that were not embedded/part of the UI package.
899  * For example, not load icons, stylesheets from the internet
900  * Not load files from the filesystem, unless they reside in the same local directory (e.g. config files)
901* A Graphical UI MUST show the blocky-identicon for ethereum addresses.
902* A UI MUST warn display appropriate warning if the destination-account is formatted with invalid checksum.
903* A UI MUST NOT open any ports or services
904  * The signer opens the public port
905* A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write.
906* A UI SHOULD inform the user about the `SHA256` or `MD5` hash of the binary being executed
907* A UI SHOULD NOT maintain a secondary storage of data, e.g. list of accounts
908  * The signer provides accounts
909* A UI SHOULD, to the best extent possible, use static linking / bundling, so that required libraries are bundled
910along with the UI.
911
912
913### UI Implementations
914
915There are a couple of implementation for a UI. We'll try to keep this list up to date.
916
917| Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters|
918| ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- |
919| QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: |  :+1: (partially)|
920| GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: |  :x: |
921| Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: |  :x: |
922| Clef UI| https://github.com/ethereum/clef-ui| Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: |  :+1: (approve tx only)|
923