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