github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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 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 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 --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 54 Example: 55 56 ``` 57 $ clef -keystore /my/keystore -chainid 4 58 ``` 59 60 ## Security model 61 62 The 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 70 The general flow for signing a transaction using e.g. Geth is as follows: 71 ![image](sign_flow.png) 72 73 In this case, `geth` would be started with `--signer http://localhost:8550` and would relay requests to `eth.sendTransaction`. 74 75 ## TODOs 76 77 Some 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 116 Clef 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 118 Some 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 120 The External API is **untrusted**: it does not accept credentials, nor does it expect that requests have any authority. 121 122 ### Internal UI API 123 124 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. 125 126 An example (insecure) proof-of-concept of has been implemented in `pythonsigner.py`. 127 128 The 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 132 process 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 140 See 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 147 All hex encoded values must be prefixed with `0x`. 148 149 ### account_new 150 151 #### Create new password protected account 152 153 The 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. 154 The 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 158 None 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 ``` 172 Response 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 188 None 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 ``` 202 Response 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 (transaction manager hash if transaction is private) 227 - `nonce` [number]: account nonce 228 - `isPrivate` [boolean:optional]: whether the transaction is a Quorum private transaction 229 3. method signature [string:optional] 230 - 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. 231 232 233 #### Result 234 - raw [data]: signed transaction in RLP encoded form 235 - tx [json]: signed transaction in JSON form 236 237 #### Sample call 238 ```json 239 { 240 "id": 2, 241 "jsonrpc": "2.0", 242 "method": "account_signTransaction", 243 "params": [ 244 { 245 "from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", 246 "gas": "0x55555", 247 "gasPrice": "0x1234", 248 "input": "0xabcd", 249 "nonce": "0x0", 250 "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", 251 "value": "0x1234" 252 } 253 ] 254 } 255 ``` 256 Response 257 258 ```json 259 { 260 "jsonrpc": "2.0", 261 "id": 2, 262 "result": { 263 "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", 264 "tx": { 265 "nonce": "0x0", 266 "gasPrice": "0x1234", 267 "gas": "0x55555", 268 "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", 269 "value": "0x1234", 270 "input": "0xabcd", 271 "v": "0x26", 272 "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", 273 "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", 274 "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" 275 } 276 } 277 } 278 ``` 279 #### Sample call with ABI-data 280 281 282 ```json 283 { 284 "id": 67, 285 "jsonrpc": "2.0", 286 "method": "account_signTransaction", 287 "params": [ 288 { 289 "from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa", 290 "gas": "0x333", 291 "gasPrice": "0x1", 292 "nonce": "0x0", 293 "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", 294 "value": "0x0", 295 "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" 296 }, 297 "safeSend(address)" 298 ] 299 } 300 ``` 301 Response 302 303 ```json 304 { 305 "jsonrpc": "2.0", 306 "id": 67, 307 "result": { 308 "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", 309 "tx": { 310 "nonce": "0x0", 311 "gasPrice": "0x1", 312 "gas": "0x333", 313 "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", 314 "value": "0x0", 315 "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", 316 "v": "0x26", 317 "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", 318 "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", 319 "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" 320 } 321 } 322 } 323 ``` 324 325 Bash example: 326 ```bash 327 > 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/ 328 329 {"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"}}} 330 ``` 331 332 ### account_signData 333 334 #### Sign data 335 Signs a chunk of data and returns the calculated signature. 336 337 #### Arguments 338 - content type [string]: type of signed data 339 - `text/validator`: hex data with custom validator defined in a contract 340 - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers 341 - `text/plain`: simple hex data validated by `account_ecRecover` 342 - account [address]: account to sign with 343 - data [object]: data to sign 344 345 #### Result 346 - calculated signature [data] 347 348 #### Sample call 349 ```json 350 { 351 "id": 3, 352 "jsonrpc": "2.0", 353 "method": "account_signData", 354 "params": [ 355 "data/plain", 356 "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", 357 "0xaabbccdd" 358 ] 359 } 360 ``` 361 Response 362 363 ```json 364 { 365 "id": 3, 366 "jsonrpc": "2.0", 367 "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" 368 } 369 ``` 370 371 ### account_signTypedData 372 373 #### Sign data 374 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. 375 376 #### Arguments 377 - account [address]: account to sign with 378 - data [object]: data to sign 379 380 #### Result 381 - calculated signature [data] 382 383 #### Sample call 384 ```json 385 { 386 "id": 68, 387 "jsonrpc": "2.0", 388 "method": "account_signTypedData", 389 "params": [ 390 "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", 391 { 392 "types": { 393 "EIP712Domain": [ 394 { 395 "name": "name", 396 "type": "string" 397 }, 398 { 399 "name": "version", 400 "type": "string" 401 }, 402 { 403 "name": "chainId", 404 "type": "uint256" 405 }, 406 { 407 "name": "verifyingContract", 408 "type": "address" 409 } 410 ], 411 "Person": [ 412 { 413 "name": "name", 414 "type": "string" 415 }, 416 { 417 "name": "wallet", 418 "type": "address" 419 } 420 ], 421 "Mail": [ 422 { 423 "name": "from", 424 "type": "Person" 425 }, 426 { 427 "name": "to", 428 "type": "Person" 429 }, 430 { 431 "name": "contents", 432 "type": "string" 433 } 434 ] 435 }, 436 "primaryType": "Mail", 437 "domain": { 438 "name": "Ether Mail", 439 "version": "1", 440 "chainId": 1, 441 "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" 442 }, 443 "message": { 444 "from": { 445 "name": "Cow", 446 "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" 447 }, 448 "to": { 449 "name": "Bob", 450 "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" 451 }, 452 "contents": "Hello, Bob!" 453 } 454 } 455 ] 456 } 457 ``` 458 Response 459 460 ```json 461 { 462 "id": 1, 463 "jsonrpc": "2.0", 464 "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" 465 } 466 ``` 467 468 ### account_ecRecover 469 470 #### Recover the signing address 471 472 Derive the address from the account that was used to sign data with content type `text/plain` and the signature. 473 474 #### Arguments 475 - data [data]: data that was signed 476 - signature [data]: the signature to verify 477 478 #### Result 479 - derived account [address] 480 481 #### Sample call 482 ```json 483 { 484 "id": 4, 485 "jsonrpc": "2.0", 486 "method": "account_ecRecover", 487 "params": [ 488 "0xaabbccdd", 489 "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" 490 ] 491 } 492 ``` 493 Response 494 495 ```json 496 { 497 "id": 4, 498 "jsonrpc": "2.0", 499 "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db" 500 } 501 ``` 502 503 ### account_version 504 505 #### Get external API version 506 507 Get the version of the external API used by Clef. 508 509 #### Arguments 510 511 None 512 513 #### Result 514 515 * external API version [string] 516 517 #### Sample call 518 ```json 519 { 520 "id": 0, 521 "jsonrpc": "2.0", 522 "method": "account_version", 523 "params": [] 524 } 525 ``` 526 527 Response 528 ```json 529 { 530 "id": 0, 531 "jsonrpc": "2.0", 532 "result": "6.0.0" 533 } 534 ``` 535 536 ## UI API 537 538 These methods needs to be implemented by a UI listener. 539 540 By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with 541 denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented. 542 See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'. 543 544 All 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. 545 546 See the [ui API changelog](intapi_changelog.md) for information about changes to this API. 547 548 OBS! A slight deviation from `json` standard is in place: every request and response should be confined to a single line. 549 Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make 550 things simpler for both parties. 551 552 ### ApproveTx / `ui_approveTx` 553 554 Invoked when there's a transaction for approval. 555 556 557 #### Sample call 558 559 Here's a method invocation: 560 ```bash 561 562 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/ 563 ``` 564 Results in the following invocation on the UI: 565 ```json 566 567 { 568 "jsonrpc": "2.0", 569 "id": 1, 570 "method": "ui_approveTx", 571 "params": [ 572 { 573 "transaction": { 574 "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", 575 "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", 576 "gas": "0x333", 577 "gasPrice": "0x1", 578 "value": "0x0", 579 "nonce": "0x0", 580 "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", 581 "input": null 582 }, 583 "call_info": [ 584 { 585 "type": "WARNING", 586 "message": "Invalid checksum on to-address" 587 }, 588 { 589 "type": "Info", 590 "message": "safeSend(address: 0x0000000000000000000000000000000000000012)" 591 } 592 ], 593 "meta": { 594 "remote": "127.0.0.1:48486", 595 "local": "localhost:8550", 596 "scheme": "HTTP/1.1" 597 } 598 } 599 ] 600 } 601 602 ``` 603 604 The same method invocation, but with invalid data: 605 ```bash 606 607 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/ 608 ``` 609 610 ```json 611 612 { 613 "jsonrpc": "2.0", 614 "id": 1, 615 "method": "ui_approveTx", 616 "params": [ 617 { 618 "transaction": { 619 "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", 620 "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", 621 "gas": "0x333", 622 "gasPrice": "0x1", 623 "value": "0x0", 624 "nonce": "0x0", 625 "data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012", 626 "input": null 627 }, 628 "call_info": [ 629 { 630 "type": "WARNING", 631 "message": "Invalid checksum on to-address" 632 }, 633 { 634 "type": "WARNING", 635 "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)" 636 } 637 ], 638 "meta": { 639 "remote": "127.0.0.1:48492", 640 "local": "localhost:8550", 641 "scheme": "HTTP/1.1" 642 } 643 } 644 ] 645 } 646 647 648 ``` 649 650 One which has missing `to`, but with no `data`: 651 652 653 ```json 654 655 { 656 "jsonrpc": "2.0", 657 "id": 3, 658 "method": "ui_approveTx", 659 "params": [ 660 { 661 "transaction": { 662 "from": "", 663 "to": null, 664 "gas": "0x0", 665 "gasPrice": "0x0", 666 "value": "0x0", 667 "nonce": "0x0", 668 "data": null, 669 "input": null 670 }, 671 "call_info": [ 672 { 673 "type": "CRITICAL", 674 "message": "Tx will create contract with empty code!" 675 } 676 ], 677 "meta": { 678 "remote": "signer binary", 679 "local": "main", 680 "scheme": "in-proc" 681 } 682 } 683 ] 684 } 685 ``` 686 687 ### ApproveListing / `ui_approveListing` 688 689 Invoked when a request for account listing has been made. 690 691 #### Sample call 692 693 ```json 694 695 { 696 "jsonrpc": "2.0", 697 "id": 5, 698 "method": "ui_approveListing", 699 "params": [ 700 { 701 "accounts": [ 702 { 703 "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42", 704 "address": "0x123409812340981234098123409812deadbeef42" 705 }, 706 { 707 "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42", 708 "address": "0xcafebabedeadbeef34098123409812deadbeef42" 709 } 710 ], 711 "meta": { 712 "remote": "signer binary", 713 "local": "main", 714 "scheme": "in-proc" 715 } 716 } 717 ] 718 } 719 720 ``` 721 722 723 ### ApproveSignData / `ui_approveSignData` 724 725 #### Sample call 726 727 ```json 728 { 729 "jsonrpc": "2.0", 730 "id": 4, 731 "method": "ui_approveSignData", 732 "params": [ 733 { 734 "address": "0x123409812340981234098123409812deadbeef42", 735 "raw_data": "0x01020304", 736 "messages": [ 737 { 738 "name": "message", 739 "value": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004", 740 "type": "text/plain" 741 } 742 ], 743 "hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310", 744 "meta": { 745 "remote": "signer binary", 746 "local": "main", 747 "scheme": "in-proc" 748 } 749 } 750 ] 751 } 752 ``` 753 754 ### ApproveNewAccount / `ui_approveNewAccount` 755 756 Invoked when a request for creating a new account has been made. 757 758 #### Sample call 759 760 ```json 761 { 762 "jsonrpc": "2.0", 763 "id": 4, 764 "method": "ui_approveNewAccount", 765 "params": [ 766 { 767 "meta": { 768 "remote": "signer binary", 769 "local": "main", 770 "scheme": "in-proc" 771 } 772 } 773 ] 774 } 775 ``` 776 777 ### ShowInfo / `ui_showInfo` 778 779 The UI should show the info (a single message) to the user. Does not expect response. 780 781 #### Sample call 782 783 ```json 784 { 785 "jsonrpc": "2.0", 786 "id": 9, 787 "method": "ui_showInfo", 788 "params": [ 789 "Tests completed" 790 ] 791 } 792 793 ``` 794 795 ### ShowError / `ui_showError` 796 797 The UI should show the error (a single message) to the user. Does not expect response. 798 799 ```json 800 801 { 802 "jsonrpc": "2.0", 803 "id": 2, 804 "method": "ui_showError", 805 "params": [ 806 "Something bad happened!" 807 ] 808 } 809 810 ``` 811 812 ### OnApprovedTx / `ui_onApprovedTx` 813 814 `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. 815 816 When implementing rate-limited rules, this callback should be used. 817 818 TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`. 819 820 Example call: 821 ```json 822 823 { 824 "jsonrpc": "2.0", 825 "id": 1, 826 "method": "ui_onApprovedTx", 827 "params": [ 828 { 829 "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", 830 "tx": { 831 "nonce": "0x0", 832 "gasPrice": "0x1", 833 "gas": "0x333", 834 "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", 835 "value": "0x0", 836 "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", 837 "v": "0x26", 838 "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", 839 "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", 840 "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" 841 } 842 } 843 ] 844 } 845 ``` 846 847 ### OnSignerStartup / `ui_onSignerStartup` 848 849 This 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, 850 in k/v-form. 851 852 Example call: 853 ```json 854 855 { 856 "jsonrpc": "2.0", 857 "id": 1, 858 "method": "ui_onSignerStartup", 859 "params": [ 860 { 861 "info": { 862 "extapi_http": "http://localhost:8550", 863 "extapi_ipc": null, 864 "extapi_version": "2.0.0", 865 "intapi_version": "1.2.0" 866 } 867 } 868 ] 869 } 870 871 ``` 872 873 ### OnInputRequired / `ui_onInputRequired` 874 875 Invoked when Clef requires user input (e.g. a password). 876 877 Example call: 878 ```json 879 880 { 881 "jsonrpc": "2.0", 882 "id": 1, 883 "method": "ui_onInputRequired", 884 "params": [ 885 { 886 "title": "Account password", 887 "prompt": "Please enter the password for account 0x694267f14675d7e1b9494fd8d72fefe1755710fa", 888 "isPassword": true 889 } 890 ] 891 } 892 ``` 893 894 895 ### Rules for UI apis 896 897 A UI should conform to the following rules. 898 899 * A UI MUST NOT load any external resources that were not embedded/part of the UI package. 900 * For example, not load icons, stylesheets from the internet 901 * Not load files from the filesystem, unless they reside in the same local directory (e.g. config files) 902 * A Graphical UI MUST show the blocky-identicon for ethereum addresses. 903 * A UI MUST warn display appropriate warning if the destination-account is formatted with invalid checksum. 904 * A UI MUST NOT open any ports or services 905 * The signer opens the public port 906 * A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write. 907 * A UI SHOULD inform the user about the `SHA256` or `MD5` hash of the binary being executed 908 * A UI SHOULD NOT maintain a secondary storage of data, e.g. list of accounts 909 * The signer provides accounts 910 * A UI SHOULD, to the best extent possible, use static linking / bundling, so that required libraries are bundled 911 along with the UI. 912 913 914 ### UI Implementations 915 916 There are a couple of implementation for a UI. We'll try to keep this list up to date. 917 918 | Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters| 919 | ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- | 920 | QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)| 921 | GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: | 922 | Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: | 923 | Clef UI| https://github.com/kyokan/clef-ui| Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)|