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