github.com/theQRL/go-zond@v0.1.1/cmd/clef/tutorial.md (about)

     1  ## Initializing Clef
     2  
     3  First things first, Clef needs to store some data itself. Since that data might be sensitive (passwords, signing rules, accounts), Clef's entire storage is encrypted. To support encrypting data, the first step is to initialize Clef with a random master seed, itself too encrypted with your chosen password:
     4  
     5  ```text
     6  $ clef init
     7  
     8  WARNING!
     9  
    10  Clef is an account management tool. It may, like any software, contain bugs.
    11  
    12  Please take care to
    13  - backup your keystore files,
    14  - verify that the keystore(s) can be opened with your password.
    15  
    16  Clef is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    17  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
    18  PURPOSE. See the GNU General Public License for more details.
    19  
    20  Enter 'ok' to proceed:
    21  > ok
    22  
    23  The master seed of clef will be locked with a password.
    24  Please specify a password. Do not forget this password!
    25  Password:
    26  Repeat password:
    27  
    28  A master seed has been generated into /home/martin/.clef/masterseed.json
    29  
    30  This is required to be able to store credentials, such as:
    31  * Passwords for keystores (used by rule engine)
    32  * Storage for JavaScript auto-signing rules
    33  * Hash of JavaScript rule-file
    34  
    35  You should treat 'masterseed.json' with utmost secrecy and make a backup of it!
    36  * The password is necessary but not enough, you need to back up the master seed too!
    37  * The master seed does not contain your accounts, those need to be backed up separately!
    38  ```
    39  
    40  *For readability purposes, we'll remove the WARNING printout, user confirmation and the unlocking of the master seed in the rest of this document.*
    41  
    42  ## Remote interactions
    43  
    44  Clef is capable of managing both key-file based accounts as well as hardware wallets. To evaluate clef, we're going to point it to our Rinkeby testnet keystore and specify the Rinkeby chain ID for signing (Clef doesn't have a backing chain, so it doesn't know what network it runs on).
    45  
    46  ```text
    47  $ clef --keystore ~/.ethereum/rinkeby/keystore --chainid 4
    48  
    49  INFO [07-01|11:00:46.385] Starting signer                          chainid=4 keystore=$HOME/.ethereum/rinkeby/keystore light-kdf=false advanced=false
    50  DEBUG[07-01|11:00:46.389] FS scan times                            list=3.521941ms set=9.017µs diff=4.112µs
    51  DEBUG[07-01|11:00:46.391] Ledger support enabled
    52  DEBUG[07-01|11:00:46.391] Trezor support enabled via HID
    53  DEBUG[07-01|11:00:46.391] Trezor support enabled via WebUSB
    54  INFO [07-01|11:00:46.391] Audit logs configured                    file=audit.log
    55  DEBUG[07-01|11:00:46.392] IPC registered                           namespace=account
    56  INFO [07-01|11:00:46.392] IPC endpoint opened                      url=$HOME/.clef/clef.ipc
    57  ------- Signer info -------
    58  * intapi_version : 7.0.0
    59  * extapi_version : 6.0.0
    60  * extapi_http : n/a
    61  * extapi_ipc : $HOME/.clef/clef.ipc
    62  ```
    63  
    64  By default, Clef starts up in CLI (Command Line Interface) mode. Arbitrary remote processes may *request* account interactions (e.g. sign a transaction), which the user will need to individually *confirm*.
    65  
    66  To test this out, we can *request* Clef to list all account via its *External API endpoint*:
    67  
    68  ```text
    69  echo '{"id": 1, "jsonrpc": "2.0", "method": "account_list"}' | nc -U ~/.clef/clef.ipc
    70  ```
    71  
    72  This will prompt the user within the Clef CLI to confirm or deny the request:
    73  
    74  ```text
    75  -------- List Account request--------------
    76  A request has been made to list all accounts.
    77  You can select which accounts the caller can see
    78    [x] 0xD9C9Cd5f6779558b6e0eD4e6Acf6b1947E7fA1F3
    79      URL: keystore://$HOME/.ethereum/rinkeby/keystore/UTC--2017-04-14T15-15-00.327614556Z--d9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3
    80    [x] 0x086278A6C067775F71d6B2BB1856Db6E28c30418
    81      URL: keystore://$HOME/.ethereum/rinkeby/keystore/UTC--2018-02-06T22-53-11.211657239Z--086278a6c067775f71d6b2bb1856db6e28c30418
    82  -------------------------------------------
    83  Request context:
    84  	NA -> NA -> NA
    85  
    86  Additional HTTP header data, provided by the external caller:
    87  	User-Agent:
    88  	Origin:
    89  Approve? [y/N]:
    90  >
    91  ```
    92  
    93  Depending on whether we approve or deny the request, the original NetCat process will get:
    94  
    95  ```text
    96  {"jsonrpc":"2.0","id":1,"result":["0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3","0x086278a6c067775f71d6b2bb1856db6e28c30418"]}
    97  
    98  or
    99  
   100  {"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Request denied"}}
   101  ```
   102  
   103  Apart from listing accounts, you can also *request* creating a new account; signing transactions and data; and recovering signatures. You can find the available methods in the Clef [External API Spec](https://github.com/theQRL/go-zond/tree/master/cmd/clef#external-api-1) and the [External API Changelog](https://github.com/theQRL/go-zond/blob/master/cmd/clef/extapi_changelog.md).
   104  
   105  *Note, the number of things you can do from the External API is deliberately small, since we want to limit the power of remote calls by as much as possible! Clef has an [Internal API](https://github.com/theQRL/go-zond/tree/master/cmd/clef#ui-api-1) too for the UI (User Interface) which is much richer and can support custom interfaces on top. But that's out of scope here.*
   106  
   107  ## Automatic rules
   108  
   109  For most users, manually confirming every transaction is the way to go. However, there are cases when it makes sense to set up some rules which permit Clef to sign a transaction without prompting the user. One such example would be running a signer on Rinkeby or other PoA networks.
   110  
   111  For starters, we can create a rule file that automatically permits anyone to list our available accounts without user confirmation. The rule file is a tiny JavaScript snippet that you can program however you want:
   112  
   113  ```js
   114  function ApproveListing() {
   115      return "Approve"
   116  }
   117  ```
   118  
   119  Of course, Clef isn't going to just accept and run arbitrary scripts you give it, that would be dangerous if someone changes your rule file! Instead, you need to explicitly *attest* the rule file, which entails injecting its hash into Clef's secure store.
   120  
   121  ```text
   122  $ sha256sum rules.js
   123  645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c  rules.js
   124  
   125  $ clef attest 645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c
   126  Decrypt master seed of clef
   127  Password:
   128  INFO [07-01|13:25:03.290] Ruleset attestation updated              sha256=645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c
   129  ```
   130  
   131  At this point, we can start Clef with the rule file:
   132  
   133  ```text
   134  $ clef --keystore ~/.ethereum/rinkeby/keystore --chainid 4 --rules rules.js
   135  
   136  INFO [07-01|13:39:49.726] Rule engine configured                   file=rules.js
   137  INFO [07-01|13:39:49.726] Starting signer                          chainid=4 keystore=$HOME/.ethereum/rinkeby/keystore light-kdf=false advanced=false
   138  DEBUG[07-01|13:39:49.726] FS scan times                            list=35.15µs set=4.251µs diff=2.766µs
   139  DEBUG[07-01|13:39:49.727] Ledger support enabled
   140  DEBUG[07-01|13:39:49.727] Trezor support enabled via HID
   141  DEBUG[07-01|13:39:49.727] Trezor support enabled via WebUSB
   142  INFO [07-01|13:39:49.728] Audit logs configured                    file=audit.log
   143  DEBUG[07-01|13:39:49.728] IPC registered                           namespace=account
   144  INFO [07-01|13:39:49.728] IPC endpoint opened                      url=$HOME/.clef/clef.ipc
   145  ------- Signer info -------
   146  * intapi_version : 7.0.0
   147  * extapi_version : 6.0.0
   148  * extapi_http : n/a
   149  * extapi_ipc : $HOME/.clef/clef.ipc
   150  ```
   151  
   152  Any account listing *request* will now be auto-approved by the rule file:
   153  
   154  ```text
   155  $ echo '{"id": 1, "jsonrpc": "2.0", "method": "account_list"}' | nc -U ~/.clef/clef.ipc
   156  {"jsonrpc":"2.0","id":1,"result":["0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3","0x086278a6c067775f71d6b2bb1856db6e28c30418"]}
   157  ```
   158  
   159  ## Under the hood
   160  
   161  While doing the operations above, these files have been created:
   162  
   163  ```text
   164  $ ls -laR ~/.clef/
   165  
   166  $HOME/.clef/:
   167  total 24
   168  drwxr-x--x   3 user user  4096 Jul  1 13:45 .
   169  drwxr-xr-x 102 user user 12288 Jul  1 13:39 ..
   170  drwx------   2 user user  4096 Jul  1 13:25 02f90c0603f4f2f60188
   171  -r--------   1 user user   868 Jun 28 13:55 masterseed.json
   172  
   173  $HOME/.clef/02f90c0603f4f2f60188:
   174  total 12
   175  drwx------ 2 user user 4096 Jul  1 13:25 .
   176  drwxr-x--x 3 user user 4096 Jul  1 13:45 ..
   177  -rw------- 1 user user  159 Jul  1 13:25 config.json
   178  
   179  $ cat ~/.clef/02f90c0603f4f2f60188/config.json
   180  {"ruleset_sha256":{"iv":"SWWEtnl+R+I+wfG7","c":"I3fjmwmamxVcfGax7D0MdUOL29/rBWcs73WBILmYK0o1CrX7wSMc3y37KsmtlZUAjp0oItYq01Ow8VGUOzilG91tDHInB5YHNtm/YkufEbo="}}
   181  ```
   182  
   183  In `$HOME/.clef`, the `masterseed.json` file was created, containing the master seed. This seed was then used to derive a few other things:
   184  
   185  - **Vault location**: in this case `02f90c0603f4f2f60188`.
   186     - If you use a different master seed, a different vault location will be used that does not conflict with each other (e.g. `clef --signersecret /path/to/file`). This allows you to run multiple instances of Clef, each with its own rules (e.g. mainnet + testnet).
   187  - **`config.json`**: the encrypted key/value storage for configuration data, currently only containing the key `ruleset_sha256`, the attested hash of the automatic rules to use.
   188  
   189  ## Advanced rules
   190  
   191  In order to make more useful rules - like signing transactions - the signer needs access to the passwords needed to unlock keys from the keystore. You can inject an unlock password via `clef setpw`.
   192  
   193  ```text
   194  $ clef setpw 0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3
   195  
   196  Please enter a password to store for this address:
   197  Password:
   198  Repeat password:
   199  
   200  Decrypt master seed of clef
   201  Password:
   202  INFO [07-01|14:05:56.031] Credential store updated                 key=0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3
   203  ```
   204  
   205  Now let's update the rules to make use of the new credentials:
   206  
   207  ```js
   208  function ApproveListing() {
   209      return "Approve"
   210  }
   211  
   212  function ApproveSignData(req) {
   213      if (req.address.toLowerCase() == "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3") {
   214          if (req.messages[0].value.indexOf("bazonk") >= 0) {
   215              return "Approve"
   216          }
   217          return "Reject"
   218      }
   219      // Otherwise goes to manual processing
   220  }
   221  ```
   222  
   223  In this example:
   224  
   225  - Any requests to sign data with the account `0xd9c9...` will be:
   226      - Auto-approved if the message contains `bazonk`,
   227      - Auto-rejected if the message does not contain `bazonk`,
   228  - Any other requests will be passed along for manual confirmation.
   229  
   230  *Note, to make this example work, please use you own accounts. You can create a new account either via Clef or the traditional account CLI tools. If the latter was chosen, make sure both Clef and Geth use the same keystore by specifying `--keystore path/to/your/keystore` when running Clef.*
   231  
   232  Attest the new rule file so that Clef will accept loading it:
   233  
   234  ```text
   235  $ sha256sum rules.js
   236  f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178  rules.js
   237  
   238  $ clef attest f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178
   239  Decrypt master seed of clef
   240  Password:
   241  INFO [07-01|14:11:28.509] Ruleset attestation updated              sha256=f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178
   242  ```
   243  
   244  Restart Clef with the new rules in place:
   245  
   246  ```
   247  $ clef --keystore ~/.ethereum/rinkeby/keystore --chainid 4 --rules rules.js
   248  
   249  INFO [07-01|14:12:41.636] Rule engine configured                   file=rules.js
   250  INFO [07-01|14:12:41.636] Starting signer                          chainid=4 keystore=$HOME/.ethereum/rinkeby/keystore light-kdf=false advanced=false
   251  DEBUG[07-01|14:12:41.636] FS scan times                            list=46.722µs set=4.47µs diff=2.157µs
   252  DEBUG[07-01|14:12:41.637] Ledger support enabled
   253  DEBUG[07-01|14:12:41.637] Trezor support enabled via HID
   254  DEBUG[07-01|14:12:41.638] Trezor support enabled via WebUSB
   255  INFO [07-01|14:12:41.638] Audit logs configured                    file=audit.log
   256  DEBUG[07-01|14:12:41.638] IPC registered                           namespace=account
   257  INFO [07-01|14:12:41.638] IPC endpoint opened                      url=$HOME/.clef/clef.ipc
   258  ------- Signer info -------
   259  * intapi_version : 7.0.0
   260  * extapi_version : 6.0.0
   261  * extapi_http : n/a
   262  * extapi_ipc : $HOME/.clef/clef.ipc
   263  ```
   264  
   265  Then test signing, once with `bazonk` and once without:
   266  
   267  ```
   268  $ echo '{"id": 1, "jsonrpc":"2.0", "method":"account_signData", "params":["data/plain", "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3", "0x202062617a6f6e6b2062617a2067617a0a"]}' | nc -U ~/.clef/clef.ipc
   269  {"jsonrpc":"2.0","id":1,"result":"0x4f93e3457027f6be99b06b3392d0ebc60615ba448bb7544687ef1248dea4f5317f789002df783979c417d969836b6fda3710f5bffb296b4d51c8aaae6e2ac4831c"}
   270  
   271  $ echo '{"id": 1, "jsonrpc":"2.0", "method":"account_signData", "params":["data/plain", "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3", "0x2020626f6e6b2062617a2067617a0a"]}' | nc -U ~/.clef/clef.ipc
   272  {"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Request denied"}}
   273  ```
   274  
   275  Meanwhile, in the Clef output log you can see:
   276  ```text
   277  INFO [02-21|14:42:41] Op approved
   278  INFO [02-21|14:42:56] Op rejected
   279  ```
   280  
   281  The signer also stores all traffic over the external API in a log file. The last 4 lines shows the two requests and their responses:
   282  
   283  ```text
   284  $ tail -n 4 audit.log
   285  t=2019-07-01T15:52:14+0300 lvl=info msg=SignData   api=signer type=request  metadata="{\"remote\":\"NA\",\"local\":\"NA\",\"scheme\":\"NA\",\"User-Agent\":\"\",\"Origin\":\"\"}" addr="0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3 [chksum INVALID]" data=0x202062617a6f6e6b2062617a2067617a0a content-type=data/plain
   286  t=2019-07-01T15:52:14+0300 lvl=info msg=SignData   api=signer type=response data=4f93e3457027f6be99b06b3392d0ebc60615ba448bb7544687ef1248dea4f5317f789002df783979c417d969836b6fda3710f5bffb296b4d51c8aaae6e2ac4831c error=nil
   287  t=2019-07-01T15:52:23+0300 lvl=info msg=SignData   api=signer type=request  metadata="{\"remote\":\"NA\",\"local\":\"NA\",\"scheme\":\"NA\",\"User-Agent\":\"\",\"Origin\":\"\"}" addr="0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3 [chksum INVALID]" data=0x2020626f6e6b2062617a2067617a0a     content-type=data/plain
   288  t=2019-07-01T15:52:23+0300 lvl=info msg=SignData   api=signer type=response data=                                     error="Request denied"
   289  ```
   290  
   291  For more details on writing automatic rules, please see the [rules spec](https://github.com/theQRL/go-zond/blob/master/cmd/clef/rules.md).
   292  
   293  ## Geth integration
   294  
   295  Of course, as awesome as Clef is, it's not feasible to interact with it via JSON RPC by hand. Long term, we're hoping to convince the general Ethereum community to support Clef as a general signer (it's only 3-5 methods), thus allowing your favorite DApp, Metamask, MyCrypto, etc to request signatures directly.
   296  
   297  Until then however, we're trying to pave the way via Geth. Geth v1.9.0 has built in support via `--signer <API endpoint>` for using a local or remote Clef instance as an account backend!
   298  
   299  We can try this by running Clef with our previous rules on Rinkeby (for now it's a good idea to allow auto-listing accounts, since Geth likes to retrieve them once in a while).
   300  
   301  ```text
   302  $ clef --keystore ~/.ethereum/rinkeby/keystore --chainid 4 --rules rules.js
   303  ```
   304  
   305  In a different window we can start Geth, list our accounts, even list our wallets to see where the accounts originate from:
   306  
   307  ```text
   308  $ geth --rinkeby --signer=~/.clef/clef.ipc console
   309  
   310  > eth.accounts
   311  ["0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3", "0x086278a6c067775f71d6b2bb1856db6e28c30418"]
   312  
   313  > personal.listWallets
   314  [{
   315      accounts: [{
   316          address: "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3",
   317          url: "extapi://$HOME/.clef/clef.ipc"
   318      }, {
   319          address: "0x086278a6c067775f71d6b2bb1856db6e28c30418",
   320          url: "extapi://$HOME/.clef/clef.ipc"
   321      }],
   322      status: "ok [version=6.0.0]",
   323      url: "extapi://$HOME/.clef/clef.ipc"
   324  }]
   325  
   326  > eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[0]})
   327  ```
   328  
   329  Lastly, when we requested a transaction to be sent, Clef prompted us in the original window to approve it:
   330  
   331  ```text
   332  --------- Transaction request-------------
   333  to:       0xD9C9Cd5f6779558b6e0eD4e6Acf6b1947E7fA1F3
   334  from:     0xD9C9Cd5f6779558b6e0eD4e6Acf6b1947E7fA1F3 [chksum ok]
   335  value:    0 wei
   336  gas:      0x5208 (21000)
   337  gasprice: 1000000000 wei
   338  nonce:    0x2366 (9062)
   339  
   340  Request context:
   341  	NA -> NA -> NA
   342  
   343  Additional HTTP header data, provided by the external caller:
   344  	User-Agent:
   345  	Origin:
   346  -------------------------------------------
   347  Approve? [y/N]:
   348  > y
   349  ```
   350  
   351  :boom:
   352  
   353  *Note, if you enable the external signer backend in Geth, all other account management is disabled. This is because long term we want to remove account management from Geth.*