From f49336459057c19cad2e66ce0ce31c59604bc329 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 3 Jun 2026 16:39:12 +0200 Subject: [PATCH] build, cmd/geth, signer: remove clef (#35097) `clef` is a great tool, however: * It is no longer maintained * No one else in the team can pronounce it properly We are however receiving some slop PRs for it, so I think it's time - with infinite sadness - to say goodbye. --- README.md | 1 - SECURITY.md | 1 - build/ci.go | 6 +- cmd/clef/README.md | 922 ------------- cmd/clef/consolecmd_test.go | 121 -- cmd/clef/datatypes.md | 224 --- cmd/clef/docs/clef_architecture_pt1.png | Bin 69221 -> 0 bytes cmd/clef/docs/clef_architecture_pt2.png | Bin 81521 -> 0 bytes cmd/clef/docs/clef_architecture_pt3.png | Bin 101351 -> 0 bytes cmd/clef/docs/clef_architecture_pt4.png | Bin 117597 -> 0 bytes cmd/clef/docs/qubes/clef_qubes_http.png | Bin 12237 -> 0 bytes cmd/clef/docs/qubes/clef_qubes_qrexec.png | Bin 17443 -> 0 bytes cmd/clef/docs/qubes/qrexec-example.png | Bin 16166 -> 0 bytes cmd/clef/docs/qubes/qubes-client.py | 23 - cmd/clef/docs/qubes/qubes.Clefsign | 16 - cmd/clef/docs/qubes/qubes_newaccount-1.png | Bin 22348 -> 0 bytes cmd/clef/docs/qubes/qubes_newaccount-2.png | Bin 37250 -> 0 bytes cmd/clef/docs/setup.md | 198 --- cmd/clef/extapi_changelog.md | 104 -- cmd/clef/intapi_changelog.md | 191 --- cmd/clef/main.go | 1222 ----------------- cmd/clef/pythonsigner.py | 315 ----- cmd/clef/requirements.txt | 1 - cmd/clef/rules.md | 234 ---- cmd/clef/run_test.go | 103 -- cmd/clef/sign_flow.png | Bin 20537 -> 0 bytes .../sign_1559_missing_field_exp_fail.json | 16 - ...gn_1559_missing_maxfeepergas_exp_fail.json | 16 - cmd/clef/testdata/sign_1559_tx.json | 17 - .../testdata/sign_bad_checksum_exp_fail.json | 17 - cmd/clef/testdata/sign_normal_exp_ok.json | 17 - cmd/clef/tests/testsigner.js | 89 -- cmd/clef/tutorial.md | 353 ----- signer/core/api.go | 668 --------- signer/core/api_test.go | 320 ----- signer/core/apitypes/types_test.go | 10 - signer/core/auditlog.go | 127 -- signer/core/cliui.go | 281 ---- signer/core/gnosis_safe.go | 117 -- signer/core/signed_data.go | 347 ----- signer/core/signed_data_test.go | 1062 -------------- signer/core/stdioui.go | 120 -- signer/core/testdata/README.md | 5 - signer/core/testdata/arrays-1.json | 60 - signer/core/testdata/custom_arraytype.json | 54 - signer/core/testdata/eip712.json | 76 - .../testdata/expfail_arraytype_overload.json | 67 - .../core/testdata/expfail_datamismatch_1.json | 64 - signer/core/testdata/expfail_extradata.json | 77 -- .../testdata/expfail_malformeddomainkeys.json | 64 - .../testdata/expfail_nonexistant_type.json | 64 - .../testdata/expfail_nonexistant_type2.json | 76 - .../core/testdata/expfail_toolargeuint.json | 38 - .../core/testdata/expfail_toolargeuint2.json | 38 - .../testdata/expfail_unconvertiblefloat.json | 38 - .../testdata/expfail_unconvertiblefloat2.json | 38 - .../2850f6ccf2d7f5f846dfb73119b60e09e712783f | 38 - .../36fb987a774011dc675e1b5246ac5c1d44d84d92 | 60 - .../37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d | 38 - .../582fa92154b784daa1faa293b695fa388fe34bf1 | 1 - .../ab57cb2b2b5ce614efe13a47bc73814580f2cce8 | 54 - .../e4303e23ca34fbbc43164a232b2caa7a3af2bf8d | 64 - .../f658340af009dd4a35abe645a00a7b732bc30921 | 1 - signer/core/uiapi.go | 219 --- signer/core/validation.go | 36 - signer/core/validation_test.go | 45 - signer/rules/rules.go | 240 ---- signer/rules/rules_test.go | 626 --------- 68 files changed, 1 insertion(+), 9439 deletions(-) delete mode 100644 cmd/clef/README.md delete mode 100644 cmd/clef/consolecmd_test.go delete mode 100644 cmd/clef/datatypes.md delete mode 100644 cmd/clef/docs/clef_architecture_pt1.png delete mode 100644 cmd/clef/docs/clef_architecture_pt2.png delete mode 100644 cmd/clef/docs/clef_architecture_pt3.png delete mode 100644 cmd/clef/docs/clef_architecture_pt4.png delete mode 100644 cmd/clef/docs/qubes/clef_qubes_http.png delete mode 100644 cmd/clef/docs/qubes/clef_qubes_qrexec.png delete mode 100644 cmd/clef/docs/qubes/qrexec-example.png delete mode 100644 cmd/clef/docs/qubes/qubes-client.py delete mode 100644 cmd/clef/docs/qubes/qubes.Clefsign delete mode 100644 cmd/clef/docs/qubes/qubes_newaccount-1.png delete mode 100644 cmd/clef/docs/qubes/qubes_newaccount-2.png delete mode 100644 cmd/clef/docs/setup.md delete mode 100644 cmd/clef/extapi_changelog.md delete mode 100644 cmd/clef/intapi_changelog.md delete mode 100644 cmd/clef/main.go delete mode 100644 cmd/clef/pythonsigner.py delete mode 100644 cmd/clef/requirements.txt delete mode 100644 cmd/clef/rules.md delete mode 100644 cmd/clef/run_test.go delete mode 100644 cmd/clef/sign_flow.png delete mode 100644 cmd/clef/testdata/sign_1559_missing_field_exp_fail.json delete mode 100644 cmd/clef/testdata/sign_1559_missing_maxfeepergas_exp_fail.json delete mode 100644 cmd/clef/testdata/sign_1559_tx.json delete mode 100644 cmd/clef/testdata/sign_bad_checksum_exp_fail.json delete mode 100644 cmd/clef/testdata/sign_normal_exp_ok.json delete mode 100644 cmd/clef/tests/testsigner.js delete mode 100644 cmd/clef/tutorial.md delete mode 100644 signer/core/api.go delete mode 100644 signer/core/api_test.go delete mode 100644 signer/core/auditlog.go delete mode 100644 signer/core/cliui.go delete mode 100644 signer/core/gnosis_safe.go delete mode 100644 signer/core/signed_data.go delete mode 100644 signer/core/signed_data_test.go delete mode 100644 signer/core/stdioui.go delete mode 100644 signer/core/testdata/README.md delete mode 100644 signer/core/testdata/arrays-1.json delete mode 100644 signer/core/testdata/custom_arraytype.json delete mode 100644 signer/core/testdata/eip712.json delete mode 100644 signer/core/testdata/expfail_arraytype_overload.json delete mode 100644 signer/core/testdata/expfail_datamismatch_1.json delete mode 100644 signer/core/testdata/expfail_extradata.json delete mode 100644 signer/core/testdata/expfail_malformeddomainkeys.json delete mode 100644 signer/core/testdata/expfail_nonexistant_type.json delete mode 100644 signer/core/testdata/expfail_nonexistant_type2.json delete mode 100644 signer/core/testdata/expfail_toolargeuint.json delete mode 100644 signer/core/testdata/expfail_toolargeuint2.json delete mode 100644 signer/core/testdata/expfail_unconvertiblefloat.json delete mode 100644 signer/core/testdata/expfail_unconvertiblefloat2.json delete mode 100644 signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f delete mode 100644 signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92 delete mode 100644 signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d delete mode 100644 signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1 delete mode 100644 signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8 delete mode 100644 signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d delete mode 100644 signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921 delete mode 100644 signer/core/uiapi.go delete mode 100644 signer/core/validation.go delete mode 100644 signer/core/validation_test.go delete mode 100644 signer/rules/rules.go delete mode 100644 signer/rules/rules_test.go diff --git a/README.md b/README.md index 639286ba9f..cc90c3128c 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,6 @@ directory. | Command | Description | | :--------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI page](https://geth.ethereum.org/docs/fundamentals/command-line-options) for command line options. | -| `clef` | Stand-alone signing tool, which can be used as a backend signer for `geth`. | | `devp2p` | Utilities to interact with nodes on the networking layer, without running a full blockchain. | | `abigen` | Source code generator to convert Ethereum contract definitions into easy-to-use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://geth.ethereum.org/docs/developers/dapp-developer/native-bindings) page for details. | | `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). | diff --git a/SECURITY.md b/SECURITY.md index d497248de5..50278b9a02 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -11,7 +11,6 @@ Audit reports are published in the `docs` folder: https://github.com/ethereum/go | Scope | Date | Report Link | | ------- | ------- | ----------- | | `geth` | 20170425 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2017-04-25_Geth-audit_Truesec.pdf) | -| `clef` | 20180914 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2018-09-14_Clef-audit_NCC.pdf) | | `Discv5` | 20191015 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2019-10-15_Discv5_audit_LeastAuthority.pdf) | | `Discv5` | 20200124 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2020-01-24_DiscV5_audit_Cure53.pdf) | diff --git a/build/ci.go b/build/ci.go index 173a3280ce..53ade2e1bf 100644 --- a/build/ci.go +++ b/build/ci.go @@ -75,7 +75,7 @@ var ( // Files that end up in the geth-alltools*.zip archive (and the NSIS installer // dev-tools section). Order matches the historical layout produced by ci.go. - allToolsBinaries = []string{"abigen", "evm", "geth", "rlpdump", "clef"} + allToolsBinaries = []string{"abigen", "evm", "geth", "rlpdump"} // Keeper build targets with their configurations keeperTargets = []struct { @@ -135,10 +135,6 @@ var ( BinaryName: "rlpdump", Description: "Developer utility tool that prints RLP structures.", }, - { - BinaryName: "clef", - Description: "Ethereum account management tool.", - }, } // A debian package is created for all executables listed here. diff --git a/cmd/clef/README.md b/cmd/clef/README.md deleted file mode 100644 index a92dcb1d77..0000000000 --- a/cmd/clef/README.md +++ /dev/null @@ -1,922 +0,0 @@ -# Clef - -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 ask for permission to sign the content. If the user grants the signing request, Clef will send the signature back to the DApp. - -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 synchronized with the chain, or is a node that has no built-in (or limited) account management. - -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. - -Check out the - -* [CLI tutorial](tutorial.md) for some concrete examples on how Clef works. -* [Setup docs](docs/setup.md) for information on how to configure Clef on QubesOS or USB Armory. -* [Data types](datatypes.md) for details on the communication messages between Clef and an external UI. - -## Command line flags - -Clef accepts the following command line options: - -``` -COMMANDS: - init Initialize the signer, generate secret storage - attest Attest that a js-file is to be used - setpw Store a credential for a keystore file - delpw Remove a credential for a keystore file - gendoc Generate documentation about json-rpc format - help Shows a list of commands or help for one command - -GLOBAL OPTIONS: - --loglevel value log level to emit to the screen (default: 4) - --keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore") - --configdir value Directory for Clef configuration (default: "$HOME/.clef") - --chainid value Chain id to use for signing (1=mainnet, 17000=Holesky) (default: 1) - --lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength - --nousb Disables monitoring for and managing USB hardware wallets - --pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm") - --http.addr value HTTP-RPC server listening interface (default: "localhost") - --http.vhosts value Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost") - --ipcdisable Disable the IPC-RPC server - --ipcpath Filename for IPC socket/pipe within the datadir (explicit paths escape it) - --http Enable the HTTP-RPC server - --http.port value HTTP-RPC server listening port (default: 8550) - --signersecret value A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash - --4bytedb-custom value File used for writing new 4byte-identifiers submitted via API (default: "./4byte-custom.json") - --auditlog value File used to emit audit logs. Set to "" to disable (default: "audit.log") - --rules value Path to the rule file to auto-authorize requests with - --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. - --stdio-ui-test Mechanism to test interface between Clef and UI. Requires 'stdio-ui'. - --advanced If enabled, issues warnings instead of rejections for suspicious requests. Default off - --suppress-bootwarn If set, does not show the warning during boot - --help, -h show help - --version, -v print the version -``` - -Example: - -``` -$ clef -keystore /my/keystore -chainid 4 -``` - -## Security model - -The security model of Clef is as follows: - -* One critical component (the Clef binary / daemon) is responsible for handling cryptographic operations: signing, private keys, encryption/decryption of keystore files. -* Clef has a well-defined 'external' API. -* The 'external' API is considered UNTRUSTED. -* Clef also communicates with whatever process that invoked the binary, via stdin/stdout. - * This channel is considered 'trusted'. Over this channel, approvals and passwords are communicated. - -The general flow for signing a transaction using e.g. Geth is as follows: -![image](sign_flow.png) - -In this case, `geth` would be started with `--signer http://localhost:8550` and would relay requests to `eth.sendTransaction`. - -## TODOs - -Some snags and todos - -* [ ] 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. -* [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. -* [ ] 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). -* [x] It should be possible to configure Clef to auto-allow listing (certain) accounts, instead of asking every time. -* [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: - * [x] Version info about the signer - * [x] Address of API (HTTP/IPC) - * [ ] List of known accounts -* [ ] Have a default timeout on signing operations, so that if the user has not answered within e.g. 60 seconds, the request is rejected. -* [ ] `account_signRawTransaction` -* [ ] `account_bulkSignTransactions([] transactions)` should - * only exist if enabled via config/flag - * only allow non-data-sending transactions - * all txs must use the same `from`-account - * let the user confirm, showing - * the total amount - * the number of unique recipients - -* Geth todos - - 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. - - Relay: Geth should be started in `geth --signer localhost:8550`. - - 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. - - The Geth API should switch to use the same type, and relay `to`-account verbatim to the external API. -* [x] Storage - * [x] An encrypted key-value storage should be implemented. - * See [rules.md](rules.md) for more info about this. -* Another potential thing to introduce is pairing. - * To prevent spurious requests which users just accept, implement a way to "pair" the caller with the signer (external API). - * Thus Geth/cpp would cryptographically handshake and afterwards the caller would be allowed to make signing requests. - * This feature would make the addition of rules less dangerous. - -* Wallets / accounts. Add API methods for wallets. - -## Communication - -### External API - -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). - -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. - -The External API is **untrusted**: it does not accept credentials, nor does it expect that requests have any authority. - -### Internal UI API - -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. - -An example (insecure) proof-of-concept has been implemented in `pythonsigner.py`. - -The model is as follows: - -* The user starts the UI app (`pythonsigner.py`). -* The UI app starts `clef` with `--stdio-ui`, and listens to the -process output for confirmation-requests. -* `clef` opens the external HTTP API. -* When the `signer` receives requests, it sends a JSON-RPC request via `stdout`. -* The UI app prompts the user accordingly, and responds to `clef`. -* `clef` signs (or not), and responds to the original request. - -## External API - -See the [external API changelog](extapi_changelog.md) for information about changes to this API. - -### Encoding -- number: positive integers that are hex encoded -- data: hex encoded data -- string: ASCII string - -All hex encoded values must be prefixed with `0x`. - -### account_new - -#### Create new password protected account - -The signer will generate a new private key, encrypt it according to [web3 keystore spec](https://ethereum.org/en/developers/docs/data-structures-and-encoding/web3-secret-storage/) and store it in the keystore directory. -The client is responsible for creating a backup of the keystore. If the keystore is lost there is no method of retrieving lost accounts. - -#### Arguments - -None - -#### Result - - address [string]: account address that is derived from the generated key - -#### Sample call -```json -{ - "id": 0, - "jsonrpc": "2.0", - "method": "account_new", - "params": [] -} -``` -Response -```json -{ - "id": 0, - "jsonrpc": "2.0", - "result": "0xbea9183f8f4f03d427f6bcea17388bdff1cab133" -} -``` - -### account_list - -#### List available accounts - List all accounts that this signer currently manages - -#### Arguments - -None - -#### Result - - array with account records: - - account.address [string]: account address that is derived from the generated key - -#### Sample call -```json -{ - "id": 1, - "jsonrpc": "2.0", - "method": "account_list" -} -``` -Response -```json -{ - "id": 1, - "jsonrpc": "2.0", - "result": [ - "0xafb2f771f58513609765698f65d3f2f0224a956f", - "0xbea9183f8f4f03d427f6bcea17388bdff1cab133" - ] -} -``` - -### account_signTransaction - -#### Sign transactions - Signs a transaction and responds with the signed transaction in RLP-encoded and JSON forms. - -#### Arguments - 1. transaction object: - - `from` [address]: account to send the transaction from - - `to` [address]: receiver account. If omitted or `0x`, will cause contract creation. - - `gas` [number]: maximum amount of gas to burn - - `gasPrice` [number]: gas price - - `value` [number:optional]: amount of Wei to send with the transaction - - `data` [data:optional]: input data - - `nonce` [number]: account nonce - 2. method signature [string:optional] - - 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. - - -#### Result - - raw [data]: signed transaction in RLP encoded form - - tx [json]: signed transaction in JSON form - -#### Sample call -```json -{ - "id": 2, - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", - "gas": "0x55555", - "gasPrice": "0x1234", - "input": "0xabcd", - "nonce": "0x0", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x1234" - } - ] -} -``` -Response - -```json -{ - "jsonrpc": "2.0", - "id": 2, - "result": { - "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "tx": { - "nonce": "0x0", - "gasPrice": "0x1234", - "gas": "0x55555", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x1234", - "input": "0xabcd", - "v": "0x26", - "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", - "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" - } - } -} -``` -#### Sample call with ABI-data - - -```json -{ - "id": 67, - "jsonrpc": "2.0", - "method": "account_signTransaction", - "params": [ - { - "from": "0x694267f14675d7e1b9494fd8d72fefe1755710fa", - "gas": "0x333", - "gasPrice": "0x1", - "nonce": "0x0", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x0", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012" - }, - "safeSend(address)" - ] -} -``` -Response - -```json -{ - "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" - } - } -} -``` - -Bash example: -```bash -> 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/ - -{"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"}}} -``` - -### account_signData - -#### Sign data - Signs a chunk of data and returns the calculated signature. - -#### Arguments - - content type [string]: type of signed data - - `text/validator`: hex data with a custom validator defined in a contract - - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers - - `text/plain`: simple hex data validated by `account_ecRecover` - - account [address]: account to sign with - - data [object]: data to sign - -#### Result - - calculated signature [data] - -#### Sample call -```json -{ - "id": 3, - "jsonrpc": "2.0", - "method": "account_signData", - "params": [ - "data/plain", - "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db", - "0xaabbccdd" - ] -} -``` -Response - -```json -{ - "id": 3, - "jsonrpc": "2.0", - "result": "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" -} -``` - -### account_signTypedData - -#### Sign data - 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. - -#### Arguments - - account [address]: account to sign with - - data [object]: data to sign - -#### Result - - calculated signature [data] - -#### Sample call -```json -{ - "id": 68, - "jsonrpc": "2.0", - "method": "account_signTypedData", - "params": [ - "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", - { - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": 1, - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - } - ] -} -``` -Response - -```json -{ - "id": 1, - "jsonrpc": "2.0", - "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c" -} -``` - -### account_ecRecover - -#### Recover the signing address - -Derive the address from the account that was used to sign data with content type `text/plain` and the signature. - -#### Arguments - - data [data]: data that was signed - - signature [data]: the signature to verify - -#### Result - - derived account [address] - -#### Sample call -```json -{ - "id": 4, - "jsonrpc": "2.0", - "method": "account_ecRecover", - "params": [ - "0xaabbccdd", - "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c" - ] -} -``` -Response - -```json -{ - "id": 4, - "jsonrpc": "2.0", - "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db" -} -``` - -### account_version - -#### Get external API version - -Get the version of the external API used by Clef. - -#### Arguments - -None - -#### Result - -* external API version [string] - -#### Sample call -```json -{ - "id": 0, - "jsonrpc": "2.0", - "method": "account_version", - "params": [] -} -``` - -Response -```json -{ - "id": 0, - "jsonrpc": "2.0", - "result": "6.0.0" -} -``` - -## UI API - -These methods needs to be implemented by a UI listener. - -By starting the signer with the switch `--stdio-ui-test`, the signer will invoke all known methods, and expect the UI to respond with -denials. This can be used during development to ensure that the API is (at least somewhat) correctly implemented. -See `pythonsigner`, which can be invoked via `python3 pythonsigner.py test` to perform the 'denial-handshake-test'. - -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. - -See the [ui API changelog](intapi_changelog.md) for information about changes to this API. - -OBS! A slight deviation from `json` standard is in place: every request and response should be confined to a single line. -Whereas the `json` specification allows for linebreaks, linebreaks __should not__ be used in this communication channel, to make -things simpler for both parties. - -### ApproveTx / `ui_approveTx` - -Invoked when there's a transaction for approval. - - -#### Sample call - -Here's a method invocation: -```bash - -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/ -``` -Results in the following invocation on the UI: -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_approveTx", - "params": [ - { - "transaction": { - "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", - "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "gas": "0x333", - "gasPrice": "0x1", - "value": "0x0", - "nonce": "0x0", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", - "input": null - }, - "call_info": [ - { - "type": "WARNING", - "message": "Invalid checksum on to-address" - }, - { - "type": "Info", - "message": "safeSend(address: 0x0000000000000000000000000000000000000012)" - } - ], - "meta": { - "remote": "127.0.0.1:48486", - "local": "localhost:8550", - "scheme": "HTTP/1.1" - } - } - ] -} - -``` - -The same method invocation, but with invalid data: -```bash - -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/ -``` - -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_approveTx", - "params": [ - { - "transaction": { - "from": "0x0x694267f14675d7e1b9494fd8d72fefe1755710fa", - "to": "0x0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "gas": "0x333", - "gasPrice": "0x1", - "value": "0x0", - "nonce": "0x0", - "data": "0x4401a6e40000000000000002000000000000000000000000000000000000000000000012", - "input": null - }, - "call_info": [ - { - "type": "WARNING", - "message": "Invalid checksum on to-address" - }, - { - "type": "WARNING", - "message": "Transaction data did not match ABI-interface: WARNING: Supplied data is stuffed with extra data. \nWant 0000000000000002000000000000000000000000000000000000000000000012\nHave 0000000000000000000000000000000000000000000000000000000000000012\nfor method safeSend(address)" - } - ], - "meta": { - "remote": "127.0.0.1:48492", - "local": "localhost:8550", - "scheme": "HTTP/1.1" - } - } - ] -} - - -``` - -One which has missing `to`, but with no `data`: - - -```json - -{ - "jsonrpc": "2.0", - "id": 3, - "method": "ui_approveTx", - "params": [ - { - "transaction": { - "from": "", - "to": null, - "gas": "0x0", - "gasPrice": "0x0", - "value": "0x0", - "nonce": "0x0", - "data": null, - "input": null - }, - "call_info": [ - { - "type": "CRITICAL", - "message": "Tx will create contract with empty code!" - } - ], - "meta": { - "remote": "signer binary", - "local": "main", - "scheme": "in-proc" - } - } - ] -} -``` - -### ApproveListing / `ui_approveListing` - -Invoked when a request for account listing has been made. - -#### Sample call - -```json - -{ - "jsonrpc": "2.0", - "id": 5, - "method": "ui_approveListing", - "params": [ - { - "accounts": [ - { - "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-20T14-44-54.089682944Z--123409812340981234098123409812deadbeef42", - "address": "0x123409812340981234098123409812deadbeef42" - }, - { - "url": "keystore:///home/bazonk/.ethereum/keystore/UTC--2017-11-23T21-59-03.199240693Z--cafebabedeadbeef34098123409812deadbeef42", - "address": "0xcafebabedeadbeef34098123409812deadbeef42" - } - ], - "meta": { - "remote": "signer binary", - "local": "main", - "scheme": "in-proc" - } - } - ] -} - -``` - - -### ApproveSignData / `ui_approveSignData` - -#### Sample call - -```json -{ - "jsonrpc": "2.0", - "id": 4, - "method": "ui_approveSignData", - "params": [ - { - "address": "0x123409812340981234098123409812deadbeef42", - "raw_data": "0x01020304", - "messages": [ - { - "name": "message", - "value": "\u0019Ethereum Signed Message:\n4\u0001\u0002\u0003\u0004", - "type": "text/plain" - } - ], - "hash": "0x7e3a4e7a9d1744bc5c675c25e1234ca8ed9162bd17f78b9085e48047c15ac310", - "meta": { - "remote": "signer binary", - "local": "main", - "scheme": "in-proc" - } - } - ] -} -``` - -### ApproveNewAccount / `ui_approveNewAccount` - -Invoked when a request for creating a new account has been made. - -#### Sample call - -```json -{ - "jsonrpc": "2.0", - "id": 4, - "method": "ui_approveNewAccount", - "params": [ - { - "meta": { - "remote": "signer binary", - "local": "main", - "scheme": "in-proc" - } - } - ] -} -``` - -### ShowInfo / `ui_showInfo` - -The UI should show the info (a single message) to the user. Does not expect response. - -#### Sample call - -```json -{ - "jsonrpc": "2.0", - "id": 9, - "method": "ui_showInfo", - "params": [ - "Tests completed" - ] -} - -``` - -### ShowError / `ui_showError` - -The UI should show the error (a single message) to the user. Does not expect response. - -```json - -{ - "jsonrpc": "2.0", - "id": 2, - "method": "ui_showError", - "params": [ - "Something bad happened!" - ] -} - -``` - -### OnApprovedTx / `ui_onApprovedTx` - -`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. - -When implementing rate-limited rules, this callback should be used. - -TLDR; Use this method to keep track of signed transactions, instead of using the data in `ApproveTx`. - -Example call: -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_onApprovedTx", - "params": [ - { - "raw": "0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "tx": { - "nonce": "0x0", - "gasPrice": "0x1", - "gas": "0x333", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "value": "0x0", - "input": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", - "v": "0x26", - "r": "0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e", - "s": "0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663", - "hash": "0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e" - } - } - ] -} -``` - -### OnSignerStartup / `ui_onSignerStartup` - -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, -in k/v-form. - -Example call: -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_onSignerStartup", - "params": [ - { - "info": { - "extapi_http": "http://localhost:8550", - "extapi_ipc": null, - "extapi_version": "2.0.0", - "intapi_version": "1.2.0" - } - } - ] -} - -``` - -### OnInputRequired / `ui_onInputRequired` - -Invoked when Clef requires user input (e.g. a password). - -Example call: -```json - -{ - "jsonrpc": "2.0", - "id": 1, - "method": "ui_onInputRequired", - "params": [ - { - "title": "Account password", - "prompt": "Please enter the password for account 0x694267f14675d7e1b9494fd8d72fefe1755710fa", - "isPassword": true - } - ] -} -``` - - -### Rules for UI apis - -A UI should conform to the following rules. - -* A UI MUST NOT load any external resources that were not embedded/part of the UI package. - * For example, not load icons, stylesheets from the internet - * Not load files from the filesystem, unless they reside in the same local directory (e.g. config files) -* A Graphical UI MUST show the blocky-identicon for ethereum addresses. -* A UI MUST warn display appropriate warning if the destination-account is formatted with invalid checksum. -* A UI MUST NOT open any ports or services - * The signer opens the public port -* A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write. -* A UI SHOULD inform the user about the `SHA256` or `MD5` hash of the binary being executed -* A UI SHOULD NOT maintain a secondary storage of data, e.g. list of accounts - * The signer provides accounts -* A UI SHOULD, to the best extent possible, use static linking / bundling, so that required libraries are bundled -along with the UI. - - -### UI Implementations - -There are a couple of implementation for a UI. We'll try to keep this list up to date. - -| Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters| -| ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- | -| QtSigner| https://github.com/holiman/qtsigner/ | Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)| -| GtkSigner| https://github.com/holiman/gtksigner | Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: | -| Frame | https://github.com/floating/frame/commits/go-signer | Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: | -| Clef UI| https://github.com/ethereum/clef-ui | Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)| diff --git a/cmd/clef/consolecmd_test.go b/cmd/clef/consolecmd_test.go deleted file mode 100644 index a5b324c53f..0000000000 --- a/cmd/clef/consolecmd_test.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2022 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package main - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "testing" -) - -// TestImportRaw tests clef --importraw -func TestImportRaw(t *testing.T) { - t.Parallel() - keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) - os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777) - - t.Run("happy-path", func(t *testing.T) { - t.Parallel() - // Run clef importraw - clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath) - clef.input("myverylongpassword").input("myverylongpassword") - if out := string(clef.Output()); !strings.Contains(out, - "Key imported:\n Address 0x9160DC9105f7De5dC5E7f3d97ef11DA47269BdA6") { - t.Logf("Output\n%v", out) - t.Error("Failure") - } - }) - // tests clef --importraw with mismatched passwords. - t.Run("pw-mismatch", func(t *testing.T) { - t.Parallel() - // Run clef importraw - clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath) - clef.input("myverylongpassword1").input("myverylongpassword2").WaitExit() - if have, want := clef.StderrText(), "Passwords do not match\n"; have != want { - t.Errorf("have %q, want %q", have, want) - } - }) - // tests clef --importraw with a too short password. - t.Run("short-pw", func(t *testing.T) { - t.Parallel() - // Run clef importraw - clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath) - clef.input("shorty").input("shorty").WaitExit() - if have, want := clef.StderrText(), - "password requirements not met: password too short (<10 characters)\n"; have != want { - t.Errorf("have %q, want %q", have, want) - } - }) -} - -// TestListAccounts tests clef --list-accounts -func TestListAccounts(t *testing.T) { - t.Parallel() - keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) - os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777) - - t.Run("no-accounts", func(t *testing.T) { - t.Parallel() - clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "list-accounts") - if out := string(clef.Output()); !strings.Contains(out, "The keystore is empty.") { - t.Logf("Output\n%v", out) - t.Error("Failure") - } - }) - t.Run("one-account", func(t *testing.T) { - t.Parallel() - // First, we need to import - clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath) - clef.input("myverylongpassword").input("myverylongpassword").WaitExit() - // Secondly, do a listing, using the same datadir - clef = runWithKeystore(t, clef.Datadir, "--suppress-bootwarn", "--lightkdf", "list-accounts") - if out := string(clef.Output()); !strings.Contains(out, "0x9160DC9105f7De5dC5E7f3d97ef11DA47269BdA6 (keystore:") { - t.Logf("Output\n%v", out) - t.Error("Failure") - } - }) -} - -// TestListWallets tests clef --list-wallets -func TestListWallets(t *testing.T) { - t.Parallel() - keyPath := filepath.Join(t.TempDir(), fmt.Sprintf("%v-tempkey.test", t.Name())) - os.WriteFile(keyPath, []byte("0102030405060708090a0102030405060708090a0102030405060708090a0102"), 0777) - - t.Run("no-accounts", func(t *testing.T) { - t.Parallel() - clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "list-wallets") - if out := string(clef.Output()); !strings.Contains(out, "There are no wallets.") { - t.Logf("Output\n%v", out) - t.Error("Failure") - } - }) - t.Run("one-account", func(t *testing.T) { - t.Parallel() - // First, we need to import - clef := runClef(t, "--suppress-bootwarn", "--lightkdf", "importraw", keyPath) - clef.input("myverylongpassword").input("myverylongpassword").WaitExit() - // Secondly, do a listing, using the same datadir - clef = runWithKeystore(t, clef.Datadir, "--suppress-bootwarn", "--lightkdf", "list-wallets") - if out := string(clef.Output()); !strings.Contains(out, "Account 0: 0x9160DC9105f7De5dC5E7f3d97ef11DA47269BdA6") { - t.Logf("Output\n%v", out) - t.Error("Failure") - } - }) -} diff --git a/cmd/clef/datatypes.md b/cmd/clef/datatypes.md deleted file mode 100644 index 8456edfa35..0000000000 --- a/cmd/clef/datatypes.md +++ /dev/null @@ -1,224 +0,0 @@ -## UI Client interface - -These data types are defined in the channel between clef and the UI -### SignDataRequest - -SignDataRequest contains information about a pending request to sign some data. The data to be signed can be of various types, defined by content-type. Clef has done most of the work in canonicalizing and making sense of the data, and it's up to the UI to present the user with the contents of the `message` - -Example: -```json -{ - "content_type": "text/plain", - "address": "0xDEADbEeF000000000000000000000000DeaDbeEf", - "raw_data": "GUV0aGVyZXVtIFNpZ25lZCBNZXNzYWdlOgoxMWhlbGxvIHdvcmxk", - "messages": [ - { - "name": "message", - "value": "\u0019Ethereum Signed Message:\n11hello world", - "type": "text/plain" - } - ], - "hash": "0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68", - "meta": { - "remote": "localhost:9999", - "local": "localhost:8545", - "scheme": "http", - "User-Agent": "Firefox 3.2", - "Origin": "www.malicious.ru" - } -} -``` -### SignDataResponse - approve - -Response to SignDataRequest - -Example: -```json -{ - "approved": true -} -``` -### SignDataResponse - deny - -Response to SignDataRequest - -Example: -```json -{ - "approved": false -} -``` -### SignTxRequest - -SignTxRequest contains information about a pending request to sign a transaction. Aside from the transaction itself, there is also a `call_info`-struct. That struct contains messages of various types, that the user should be informed of. - -As in any request, it's important to consider that the `meta` info also contains untrusted data. - -The `transaction` (on input into clef) can have either `data` or `input` -- if both are set, they must be identical, otherwise an error is generated. However, Clef will always use `data` when passing this struct on (if Clef does otherwise, please file a ticket) - -Example: -```json -{ - "transaction": { - "from": "0xDEADbEeF000000000000000000000000DeaDbeEf", - "to": null, - "gas": "0x3e8", - "gasPrice": "0x5", - "value": "0x6", - "nonce": "0x1", - "data": "0x01020304" - }, - "call_info": [ - { - "type": "Warning", - "message": "Something looks odd, show this message as a warning" - }, - { - "type": "Info", - "message": "User should see this as well" - } - ], - "meta": { - "remote": "localhost:9999", - "local": "localhost:8545", - "scheme": "http", - "User-Agent": "Firefox 3.2", - "Origin": "www.malicious.ru" - } -} -``` -### SignTxResponse - approve - -Response to request to sign a transaction. This response needs to contain the `transaction`, because the UI is free to make modifications to the transaction. - -Example: -```json -{ - "transaction": { - "from": "0xDEADbEeF000000000000000000000000DeaDbeEf", - "to": null, - "gas": "0x3e8", - "gasPrice": "0x5", - "value": "0x6", - "nonce": "0x4", - "data": "0x04030201" - }, - "approved": true -} -``` -### SignTxResponse - deny - -Response to SignTxRequest. When denying a request, there's no need to provide the transaction in return - -Example: -```json -{ - "transaction": { - "from": "0x", - "to": null, - "gas": "0x0", - "gasPrice": "0x0", - "value": "0x0", - "nonce": "0x0", - "data": null - }, - "approved": false -} -``` -### OnApproved - SignTransactionResult - -SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)` - -This occurs _after_ successful completion of the entire signing procedure, but right before the signed transaction is passed to the external caller. This method (and data) can be used by the UI to signal to the user that the transaction was signed, but it is primarily useful for ruleset implementations. - -A ruleset that implements a rate limitation needs to know what transactions are sent out to the external interface. By hooking into this methods, the ruleset can maintain track of that count. - -**OBS:** Note that if an attacker can restore your `clef` data to a previous point in time (e.g through a backup), the attacker can reset such windows, even if he/she is unable to decrypt the content. - -The `OnApproved` method cannot be responded to, it's purely informative - -Example: -```json -{ - "raw": "0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed", - "tx": { - "nonce": "0x64", - "gasPrice": "0x1", - "gas": "0x1", - "to": "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192", - "value": "0x1", - "input": "0x", - "v": "0x26", - "r": "0x716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293", - "s": "0x4e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed", - "hash": "0x662f6d772692dd692f1b5e8baa77a9ff95bbd909362df3fc3d301aafebde5441" - } -} -``` -### UserInputRequest - -Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free) - -Example: -```json -{ - "prompt": "The question to ask the user", - "title": "The title here", - "isPassword": true -} -``` -### UserInputResponse - -Response to UserInputRequest - -Example: -```json -{ - "text": "The textual response from user" -} -``` -### ListRequest - -Sent when a request has been made to list addresses. The UI is provided with the full `account`s, including local directory names. Note: this information is not passed back to the external caller, who only sees the `address`es. - -Example: -```json -{ - "accounts": [ - { - "address": "0xdeadbeef000000000000000000000000deadbeef", - "url": "keystore:///path/to/keyfile/a" - }, - { - "address": "0x1111111122222222222233333333334444444444", - "url": "keystore:///path/to/keyfile/b" - } - ], - "meta": { - "remote": "localhost:9999", - "local": "localhost:8545", - "scheme": "http", - "User-Agent": "Firefox 3.2", - "Origin": "www.malicious.ru" - } -} -``` -### ListResponse - -Response to list request. The response contains a list of all addresses to show to the caller. Note: the UI is free to respond with any address the caller, regardless of whether it exists or not - -Example: -```json -{ - "accounts": [ - { - "address": "0x0000000000000000000000000000000000000000", - "url": ".. ignored .." - }, - { - "address": "0xffffffffffffffffffffffffffffffffffffffff", - "url": "" - } - ] -} -``` diff --git a/cmd/clef/docs/clef_architecture_pt1.png b/cmd/clef/docs/clef_architecture_pt1.png deleted file mode 100644 index e40e532f3051d6b583698b539ae97bf12560f740..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69221 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

*@V)cs~^7_udH`|DL0Y7#oV`hCx? zBw9LbZ%2B4t@0a7E=Ow)V~=9RKm}p{`;E4qDzmE<9enV`uTk(5Um0f}t>WoX#V!W1 z14aJs`}!XBL)2OpK!7QsRJY&{%X=4wj!8iECkkVa>Q88QsZ~eHVs?s$zBGY5jx`MZ z%HD_^QCJbSLKN$3Y7?HAltrJI92IUnJcU;g)>g!VEJ*{6J!RL=qj?!R8@~Xr1BzuwW-(xMIbJHwx73%J2c(TeHvBebP3SpUg}b^Ef` zgr`cm0^IU0b99#Fcv*>fik=eMm%rs!96dF~~IZqFs?gbdB?b2O(VSxuv7 zbY~{MA%{MS9^MIIj`s`@A2{lv7nV-{Z{b!}_Rvj*de=N&y0&1icJH-2^^vUZSUuPc zy}}xEOG*e`f83a8hrK1OhQ?W)KCFV1mh$z3%GYeHt^|68Yvh)GorMiG-{ykX-k$5j zC2GbBo8^geBvCi1FK0FdD)eP2rxl4L7A>R zYHvY}>#Vi_<(KM%TKcRap185goW1_8I9gXs2K$d?R2s4iaV5RI$_hXkSy^07UOEkZ zeRY3T2^@XPB*Td3CKQ=OgO{4SD*?^Hq}i|AYHOWU@F7%W$6HXvw**_`y<3&BmYldR z5z7=9z34jwfCjf;LLh?~pZ726zl+n+q2!WYru^kx%e<+r`7@ZQ<%k*WIb=%mLHsjfc5z^|q-8OlO zskjqh`NP`I(Pl{YXUBa-YLZTJF)(LU0S}_U?G%4SCttY(`dtxx^e*&j`8ZpwlP5Muw|cRA9?@&lbmcJ|4X#``&X=FLd zf{>ydWn<-fIf~JsNRk!Rr5^KOHFAfjm}i=82@mT*JaU>{9VF6j*Pm?bq{oLEx;hNS zy5Amms#u&^1SW1ur6PhrmS2#o?+5GJ+ z(hVNnJw1P7KyGeshW`A4PZv*5P1QeRCkRU_#FqsVRulb+$sjjKM~rYzlone|^m;zi++5hB67!xlA;VLYTddFxTkHJ*I+KggBw%bJ$r>%;GkKeCvJ+pUgxNd`&DVclWiKH)HiEEQTG)mKVGp_^RU&+j0DaA_#U-S@&5OKXX(KG#5ECdHUV zy@#`Zt3`mCVm)7E1-G>4i4P8(q4TNP5Dq`94xJ6K?~EDFMA+Lg!t&pzyN?%@QFU5# zIkzFHYpN-X;Jmn6w)6F89~6Bt{aEBexrV0V(!pZ)roT`(8v}_6_u#?Q9CkScOnKc9 zn@&k0&Y&ya;bMFUnvq&)c4as_J3}7B2(L=SpFN|pfs^e%_h+XLDzn!0Ajb6oD^_Cz zKr?eii$)mrA3ykB8tH@>5z+graZbLy60lIm%NB$9;6hSsskH#L94htysT-kST0Tl? z!Rgl3N9U#UT07S_+{F-wN%q4}X@=3&`BEg>x;jYL=ye&a_2P$zIdjLHY7a#YxY9oc zOYxGH{C3VSTMu z?%5W!k(*fUkl$cIkX7 z^SX#g@3}`KM(^2_lOdrye+JNxW?(Of0C85>{qf>V0Ynzu!t{d)d=?7oJ+X;}smwnQ zo+hzSTY9J(vW0=#Z@$g}1$4s759?Y{c@1D1Aq&W2qoe2Tg){m|2b;U`RayxbqSz%a z>o*o9-%zHzk7Z{&D=QZE4i2YhJFTt$qhDM;QLhr#PY42CB0tuzaAQZs3U31D<|Nzt zLtD@Yri#VQK|Y8L^hu)g@#}jPALS-A>&`-~t%Gz~?GRMmyPa|J3k^Zw$l^nU=^$^( zby6Lkg#>0recQcSjEzkg5f*!5vCKK)ZFSOhP8?T9Tb4;xej>HBcslay8Q}pPaP+ie zbg>HMaR2;PR~uKHS}5eW7{*v&xO1A`RmVuj$xWG)Q)@N|m6c|z)9YRX-^y0-?FI)u ziZX&i?*$*dZhF9q(ammt)u&IYykV~?l8^`g?H!ceX-|Z-;mytd3cZUfA_j|5%7_C% zY)HxDqi_&TUF@+04(Qw?seUP;BvDz!WBetdsAu$8b!dDeN-x6NO#E%<1}>vz-PrYU zf~OXb5Xs^A`qJy>>zp~3(=2tx5nP)JhWyLY4+auPlvmDUjemYf?VTyuo!Y&b%h4*j z?)cT;?C@yVSwuJjCAFVrKBpP$fh-=BDB8&{(N*`55MH2Xf%GxM-m>D9+w2iURQMIE@yG`;r52+q03zxu8QiTQCuFrNfe^4l`Uy@-l72*4 zP-Z8Ki4g%)HBOHNZKBcBfy}q|Q561?CgJt3=mq=g0k&+iI<#v>f$FiA#A~vh{duwG zo)_7ZM^uQiB;mM|^n#&x$T555m^5pj`+hgS#&5L7{3dFnMo!+4P$>|mr=t_OWq>O&&4g3v$uBz3nSBj-gX6#J z6vT!ZtoFt$5QGo$kwPnTa#*{1jIu90nH-|xNRCUWf1~*#7RZ}e;+m_U{npj)=+OW( z)>q%D*ikXHj=bl`L1+)wAr!6|#P-`l=d6cyQYzY$h%s)ti$jP{()`P=fHLlx?zAY61+a|yxJeIGQRgNw|*?Eb>$}6q$pd`mGa~r#qMc8r{6Dj zdp5Xw5FH9BP71+9f%!N#O|I;OA1FkoWC)_DW4{%AVp*bVFCA135@=ye#2W#nIp6#= z03j6Sw>v4@Y+uX)%BnpAto0R|5?<)kmnm4~vkE5qO8+X6*K?iO?&{A}Du1S(05oQQ zTf8#E6xX%n*|JKw*8AptM zd&PjOUFX^wFQvzO=c)Y`6>pZ}n=2`HKKjic(3c}_rIT*o<=b`6`wwR)J@7|20L%(= zuZgWQ_?MP`M?D2ponV(UK{gK1YL$6|CFr}rWXI(G;@Us=3dgS*I$SQY?`#vUpeLOJcj^uI_)Y!0Sw z4yAJ}7`-Kox~bi56QS?>V4MEt)pwq-@zK%90~r?YJ;!huPOg&I;d}2dO~jVvfju7_ zG6^9E86wI+>&+^GShS|Hf~nKhjTY&5a`1Z#C1n3QGW~vFSRw#K&JMJdYVOYJn(z5Z z_`fg??Lu#rQrI#dkIV>nm#APN=S1`&H=OxW&b=e0E)0Xeh^$Mn+NA|_1j8#xa)lDL zzk|fW@xfA7*rBsP69`*Ku^&P%xG&>5vmspdnoT<70ak~+u56ihxP4CGlUsT~{)B{A z%j=$F`e*4y{)X>uQ7sKKhtJMWK4=89c55-xXX5%Dbu)^QX$3{Ob14CE*W~@1T6!B8 z{dHbIPTl*;EfkiY88IEC3DpD{q6>`-9A^m3UBiUSxtMk9Le)*Jh9j$j+~9J%^6zJj zv@tv^)e&hEEfTj&_cJE2@n5gq@5~v&Axx9+nq+UGB#sCNq>q*(p5CDKfvR^NRSJyD zRk`Z4+v)k-lYELUk%eG!!fKTDdmAD@FL_l{lzuu9HEZYUgUg}iIX`9kbFbs-*U1tuzE-0xqrwxT7S9qKkEajK(z+B*0-1s^{=P} z?W40q1)zkAf>%bFuYNGKj%$C3`QRSQ*u8gGcWU4`jO04$8ig0M-idAaS=!E9{@!vt zd<-!HR2|$?#e!a68Poo&##AQR*uFTDQu}MS>Qj8R>&1xN6Lj^O_m32di;3HGXWpyL zP6Li!E`94$_1o1sjUE5%vnw~7fw>Ui#l_f_p-N}HInRcP;!TtlAU7SgpFFDrphrVA z>^DqOISC|EjB!6rqU%q$nRNPwY~v@8OrhXrp0{)me$=w2T(B*OUb}y`zRgaEY$8Ir zScVq5V}$SM=YB;F#&glrC)gNYWIc3fnS#6L02b&ti3C>ScueGN2zI6(qeQ%8>|RhG z5YuXwRG{d_$Kz7o(s$-X!kCt83`noJl&i|V^Ps&@?)ITqOTVg7cw#P>|YY4EBPY0 zUOgaPyvpvTT)ti_p}t(?@A(+e{X}-t!=zpV%q>;z5C2}jndk3V_>&Cdy4a|E(8KGV zsj3PTf;j~bvRpfZL6M59Icq!nw^0L+tgOIRA$HoZ>0O7qk`niwp>;n?eDIUzGxRkw z4`Cw;6VOw3Y~*Pw=*9-?;wu+?dT@F+PIJV4E5hUcJC~D<)_E`0Gtt7j%Yz*?`isnQ zGQOkJucl1_hY== z3s)&R^4EN(>nqfB|GDJWjuTmK`haULkZ+S55Jr4DnJCo*;i8Q7O{e{7&u^CGt9J0S zXjjIIBGyc;7b2Mrm(FIB#*zPvM=jQq>>%GWnH(tlAn*x6BoOgVqn+ylaKhh_#Np^> z4gKU&PqIvMH?itLEF7DmFA*GNmtK?=cTOYBr__pCQPZ)1WJ8u0_u~M27Pl6j!YFj^ zBE(57wid1FqJ7nzoONhF%c_v=X+Pf2Q&y{26pG9z50mn z*&)Q+s_kfbe#(jzei3~FypAWQC zcy8tg1#16*!Kh9E*McPJ5wQG&h-G=*{xNv&_a~ShfBO3;5FfUEhVTRJnaRg;#FkX@ z{-I0I>(g(fT9~ywjk-~tsyth*KMwvQyrlx$!_wW^PC>v9*qws|HU|+pCF&U6EzuN<(e?4xES9`e%-kd zRJ15)^}rO>fCFWuSqP3;8ORDNY8d@P{iqfn;MFQL)z=yGj@I?~(`zHbzuNd#ue}G8`;-H%zH$;a*9XN4_`}*Yuk}H7^QNF#-BYM?@t2jY0|LFdmGq?#> zch$VR?PMju|3_gs37hmLB}&WzFJ;M-`A6N#KypWFJs3sJo*)#@D!`v?WNm_FJ4}2| z_)`mUuy~X%Cv;l8Jgjmhec_MgMi5w=Ae~yX4CUxVF!nYVZsBzYoWP&p*1x(HSi8Gx zS_9Gl8`d$a;Fx7U>xz+=gdqAD2Zc|Fv&%t*W~1o95^keWw9>p2rV1i(SLaG~@YC_G z*Z}7F7rJoFDVr0jx?GI0jgNO%FINw4SSBfN;vkA!dF$5HFh${T>R zrQ>dt(EXQ8U}%6+RdUg@eOo&3C@E_Ki5k52FU57us<~VW(_12rJ7&O*kp)bFi(7(& zg54Fofeo)vY(Tf9JNi|ic1aIS2wN6$)rbLkZ{VC9EP-myE*o0u7Qsjw;iF;m|U-~ISRfY_6dN`wvufO!7-}a%XLrWbi0mQ{&r?W*rLgH_g z+{eHS2hV9m4w=&7^FYDaIyG3E%~`{aM3N2i$UO_dG_64wn#?g#r2GQ$mlY++3uqUY z?3+-~!td#$zOh}9BhksV!>kIx@hFDTm23;9{k)L1Hw`hvZ+w=Tn*I~1-7oxr?^a#| z7ike5-G=`D58I8ldq_VTR+GC<8Ff0cIq9?gE(OH5>?Uu+0k!fgqDx{kN@s+0G6rf& zw2v)(|KHyEY_6s8rm1)n_cI|ZE$Ry3;=;hfC2~1s9=~GA?HLcS2&Cd?C$=`-wMwz> zt>2u(^st?O9(@$+G8%d)7iy?LdO#c;xmhpNM!AL-NfeSvgXu+Op_{17JO2wLelcUn zBit?)9@I&hN6Pr?oE?ozm9`^X;#Yn0N(^HcYS<~Op1^&3h1l{%bd^#yJK~+mpIx%z z8vHGUZ!G|yJFY`t9OwAtW6HB0%!oTixL{b%_89lBfo#qi+t*;F>ZxakClB{`H^(5e zkj$)%xYilDr)4|roX?2h>7wy^8Uy7MPEEV=Z;T$~oS87|8QR4F!e$JKh7Zp5<}`7k zQT;*T$xZtKH|O%yvi}8bsa*i>YQ49>gAI}LsD_SJEn*A4WFx=F(zXHA!-W^xo#ngz zb_o-82GmjchI74pI>Y-1>+cdhxgnY-sgo(fSXV-;lxqWsrf;%7WRS{`nrM~wq8FxA z;V~c0dmGa#C>;d`qO!4a4UK!X0pI7@JB8rTM1(~p=kPPS$T9+Jwv3wNotUHsfRzl$ z$8Rosn42$>B0uh5q}6=pxuq36_v@zWJox>nwL%5NoF8;Y3I zlu5=OJK9KfGi;GGH^{`z8m-fIio%;Ck-kyNpUiL455)kyBVp>5AuivSQSnKf$!Tp! z@KDoj67ZVBROG3n&;ujg97tRuELEc8orceh^6-99JV=(lDqBx|r-W;wu%0n$aotk# z`rq$%X{su{^&*sl#lKGEN5~ez{iZ5aX=OOS-Tx&b1T#zoej@~Dwz>6n&li#njg&l( zbhQ=vs7WcqaI+bPX%tWT!B4leWpx2ZCAO1u50N|sC`U<|9H(BZ(hM>n``&?@A*1|F%fo-Drz@ggZQ6)$uIQ^ z1EGy0atUsBnXA?U6x)Y6dXheWK0Cr@;zJ8z>~RO*ryeMnlONAL15~}UMz`^4NU((}MTf+i?NH z@88yJhW{!Y`6-*)4m(YU40Mf0@9h`8S((0Xx~t$HK}#+Br`jRl6hTe7b}#UwV6}mX8?$D2RvG3m!6 z!tTO*2qNjFU*fl_=F8G4keRMQxeuRKBkn%}f}S$Q`b@Y95WS9CA5t-43c*s~9Za+E zr=52fIb7|ADh2FMF?|91rbg7is}DvC(J9%5zxUS}7n$K0Z1$~XY-Vg9cZW9^aq9%8 z`PJQv2)kzdbjs!#)DV|rY49AarY^KpC)7sBtXU>!$JBOD=(ZMs%mE4%-B!CHvf8`A z8*8B9hvUUYTux5Tg*GqlVw6C&0=e(w@81Ji9o~MZP zooY*v!6!x zmBWhx>y;2K&Vd;Qfs4Ne@jwClOb}%_?~`iuBrMvd!M28nd}nqT{!qn_X)GAQg`)7^ z`}KB=1}$gP$#>c>ZqU4As!kQC3#Q*x1-0rJ#uV?>`>ZmoM4b*)hn>TN>)j7q{?2 zL%h8uZ0+pGSXff~`&FNE|FE{xAY4lQ*ei_IrS?eGoqj?)Iciiy#udmZvIlL>Dxd0D zW~BTPqT7AfT!sPW18n0Jtq;!25VfRi0Lp3Klcm6Z*HY-l`ubOwII^h8Pk$x_ovRqn zHu85cbGR z>s=pxIpXwPepR(iYXmcpqdg=*t_bG$+uJaE+80lkEG%%h6ltp4m3&Qc?&F65Xf=_BktDHcfC+u!d--(w%&SUWEOOaA7Z@8?_QCVgd z74GGuLAl`yCdwF~qcmNx_q^iTWc55Dj9VF2&-Xdz@F((@Vb&t&i|HV~3sBYF>V#Lk z_$1m4CLdaHVOrYa3VnHdB^%uhn=4%r8DdY^oKJ)MA*Y_kn&w|Ia^WK8egLFnaV?LX z=wnJo@QR!nzf#btI%9Y}Vk3u}cnr&cDr6K3G)zQ0=am zGxt(cE4T(IcMmc%@N+mT7yse|1!LKkT)hEhVcFa#j=_VhE$u>==i`OSuiZyW>&S{F5uB+Vky$qx3cY@*YvlOb{g*7%d-hh}dX!j}@ZlDj+jn-PuetAy4$$P!oP zk*RJqHC%ytY(TW7G6WRm&BYN_hut9l%*CB6ybVFY;M92HlB(avAK@W`{1& znA6<9pJSX|#6@D1ysJYP+eJ;9nNJdpVh<*#AQVhoL48xtK8F=ik;}ZD)MzdH3T>vm zkPn$FTi*3@M<+n^G0N<>ZT>1NN-%U2jJ<6AdNcHkab9K`CE{!-R$ez2{Zn12yltyw zKGF##nFr`JC0^Hk57gnD0_&~-?w^+sf(d&FAx-?PgQL1m-*$0*k&Wry_j~RaOko+B zNO~4Cv&IK~^JRa=l8Wh*^4vxUO~(jrxON?@@11ilE+s#k$!lUk`lMeLR^(luZT=-z zu=d8g@G|TC&*R;8-om>R{I3bu~q5?w(#|Un2<+ zm!v&84c$V7;Bis)dcIqcL0N}}`F`e?zt_&?3P!liK^?@#!ix4(hqh^5IL7W_4>Z5D zJ*dgW&&a59lD^Lf;H-6Yo)|;wT2G99cIw)Dj5F`cdW_%Rq66CBzOnyWgvAdzd9ygh z`<5~~^r35S1hMSg`Q)YG0;Ca4yoP@mmU4g%fT7(COHa{tRgZ;Mc77RR+*fWo=tk7y z0zx_Q9{2&ri|+NdEP^F^t;O|g@h>zF(sq5@i~UnQ3<2eXB?tJU7iQ7LKgcbW^J$YZ zvAHzJ*Le!Gpz2WFiBkM=?_xpT7}RO>0WU|T8%e#$p;m6Ij;TG|CqCky9U!1HvR#Aw zjY@kzl)DKtcTGF+9&O>OD{L6_HeE2Uu1z(2#`8ujloW7VJsg#Mf~9Mx)XTLEkkH6OI;IXA?UXo)bQA?=`MV z?v8DXKb3wyFL}0xIi}w65!oza5|?e)HQ_deLo3bIXK$&XBiCZn^af{1|H(T5aWz@E zv$A)GW+-hd&lk*G+wVSDObgd`gO1PeoC&?U0xLzEP~B0szbu{c z;*LI6i6%w5D`7`i;J2#ZpYwZ%FCUijY9H(dp=3Yy(&;kC`{t)nN zmco;$x?N%5?YuDCT|s(!&mHTETnM6c9MAjvl;iM&o`>3?rw}qE@=3V>QU4jyAN2Z} zcsgx~1%OS>NHqU5+kc&Uw|`S36wPdX8@ed>1Rr9cp;Es_3V0J+5cq{gx5&cbFg2z2 z3BZVTVSxMZ+6dB1HLXO<94>abGUbE8I*<{!7wDOt=JAq;0~HTcTSU`m%G64xzl?K= z8Y?viy$b*mz03|y__bJXFwpEVmc{Ud!Hp0+YYxAn+xIpgkWpE1mFr9CKGu79=!MAz z3d?s`BJo58Dr^m+ILpL*obR%6uN_WEm?{z=l_h*jzzpu6Txz-sAJmD+9Bm1)$j;3$; zmq^A<%qJRocJDENc*Cya7FhgQ{_^agV0eF(@Ldib%+2bu;i1&o@j@U`}u5WXX^^n$1Mf@g8aK$vyNd2AS1ZQ3TdXK@K8gBvd7LrfD-8XhG=gFF*gyN4<1jhK++RLJ8$EP7Gy7B zhS6w>L0sEqyAg9rt1}*9=pI~yo0{EQHepZMEb+?i$1gTcoCXy$pX~*M{WEukZ|Lxd z3eIE)LNHY~p3|Sd-fT;?Bud6@#w{LL*rRjCIi9$}C)sJ6=|8j&YR&x}M3;>BwLye8 zl%#DnOZ*Np>s@s(5fNbq%5X?)>3G@yFxY)6cb=NmTf)3UM>VzgFDSmq3?pGB)xxN- zvnSJ$ypD&Q1n!u+X4zi`%gnz7K^LJN0L$C?6%YBS?hGERiR0Wt(~h{!IZ>fFL-x z70L97P%uztfhljN{jDI_9OO`ryf&oz3rLw@0kKIsq1T~a8f3+U+n#eza|9QpQwP@$ z*_VWJCB4WGR(>_2m*8|ep#1qftc>Q}u50zFi2m2y=si(2M(y^USH%KSyq7!`Q)ZT- znhSyDir9-?yOzm;*3>_C?eRH#IDM;1P|_X|UV?!OrUb>hu{YvP>6zY`^%bv$kXOF& zZL~pSHcBVb;qI=t-|{zXeXMN1KE@UoPbwJ`xOu5y0@SPc`nrM^Q40+aJ->!wz#wtD z(Yc~Uz_yR|k8u)xE!hv-^F-`^7*yQse87jGt6Lv5I!M=J!dtks$g58e=HF}9xorLY z!}0U%Tuxs0eR}GT<%7m zMJ8?8F^&03>p>$7>4bRgCIj1;_opqL_v*NABWht1d%WfW*5`Hc>4M-RlR(_0D*NGfqjwCT zlTW8Z$$li&Hs78(kDdYfT|G-FA8%oUH~2r{mMQ*aWKZ|4_irrn7aCz8Hd?Kh?^Zt{ z0NW(^T8s+>A*qG2%T)Bfxxfg8Gmy?RoBlGVtdBDPr2fK53oWpmEQC?Idh8JO(ir2o zi6Pri<|gq7H$^f{q~EBV^mPiQay`=2j`2(Rm&zEadwi|qrK%=0j9qg zmtA(1aHJph@^(eVUZA8f4D*c1FBozMKB|M-#W6cys}+kltrNdklcnxq>zQy#DyPF* z)*4@5Z}N!=W_$+)!X}9lf3>-#<5MsXoGk>zx zQi!+Q1a*v$La48VXf|^;=X&l~E)VR|SSkv>ckEsL@@;o7>)h+xqC8C#M1&@F_yf3{ z)xWq4gc%;LWo92%oGl~q*T@_&a`o4gPmIq~#bCo?R?oORu_eNAnL^-V4fS7et*x|R z%t%`MNL8_*YhBC75$Ada31la#eI%%X*3T{xA191CO%UNf>wI^SJtPegu+PoP4Tn5N zibMoI3Jk}s5^iC1P@dr)U!4BN(zo;rJPHH+dSp(5s8QSI3Rk@M6l+?B#thiOPYKhJ=c&BvQHh~yOS5kfCR$a z@E|~5$Il+!m`rgqxLPRPzYfX}+oN1|2yaF7i zpw%?x2;CcMC>Q8rZ#b5EeZTTSKn+Z&3+$HOt%??lXuxi@-RkNpf6>7zRWyd|cl7x` zX*bzMCF#FCzY&zJ+pDF)NwKigHn8wG!sa^rcPFP}Hm?QfI%=sr#PIBoTMQ2Q+oq{gyZqfDR^A4DH?;|KQlb`m2*aGM8eL!(4^7KU#H& z(Yrz~)zje^n#)7czZ0H%Y@92f>x?;eVQ-=Un|Xsm_b03ub9=NzpeiOTy_VNHL|sF# z{u#3z;1NqUCu(C`_-?HudKKC~$BDA4ersU(;gs%BFg|5o!r5uD$#>n_?PbW_!9klj zVp?`c3C<+Y>p8dFegY~-cL;ykQt1^sSF(6)^-D=sKsOJ_F2xI08?zbNp`rb8w34g) z6J59bFG~6s7nw)}tT4~70S;(v(KqJ1C;A8~D(|bY8^UBMUM>Pkdr}7arfhKDt$OGw z;frx2`y-$*N@cAU$N z{*iXq!T74;NkwPHL8myxt_&d9Ej56{IhlDjKF&x30><6_`Yw{(fS`HyG5Mx!;S2_( z<`|^;(WwL&I_moUJRnx-Q0wauVJL(mYe`hUR%u7NE!u1|xV8s$d?f?dPeKx|StQb& zkvkx2Yr@#$x~8En(h4BM#Qk0U%b;G7uH-irqM_^xJ*%dccPX#s$tSC|0Py?T?O~m8 zhkJ@oJ0K zh_nrp6D%cQrJA%Bzq?KfKM1DnWrk1yVNDYKOXu{g zC~h@t%_THZWys?HZvo!0YxQ>c$%C{Gd4;*Mk0+K?z^k`wQ84Op^&ch(`n%M68jyKk ziHmfE=*~Db>4xu_GH*NvJQ2>;k+Ap#0<74*ms`Ut#eh5N;=eNTBAr)8I0TRUNl;_S zjx1zF$!b7(|A!fe;#|~d$gDhaQ%=sE>_4nQwK&rK1P#FkD(CTZcsSR00nZo&m9l!+ zi&fv{HYMy^WIQJ|BSNvKS03b&agrq}nou_j>P^hsewAV#@|m=X?s7D-T5$Ul<84{r zHmhb_(*vr|Sk&(*I4tf_UGqr~$%xZq0jt9j-31hD0iw1LC(O%ct?leM;^g0}J7krM zgse{%k8a_SWqri>)|x$9J^c=Q30MEq(|0V4k&iKC*n!i}@(bTsFo`XLF=OiK1yWa< z8j&gjPkZ{C{Vqg3k_~$ME>&<1GNdY_R{g8jMW96g-BrEYWAfUD%7eUl0il*iG-nQ5 zIVB-*Ol;Vure(L2`HSMJK?$pSsLffq(53wH4dAm2G0XtERl|aX6ss~ZNrU@CS{lEG zP&_Cw3W@1@iJf_biGJcW&y6}>a~MehLtP`+$&jqj)GDymG|mWuMP+2?&jUW+#D+iw*PL9^HnE75RmrZ-peTr-M6`s zGxZn7Lb78Af=co$v0<2g&yFW|__g9H-CXbPK9$NBP0Y|~QH8K_Rq)N*!!mnK^7tPp z89CjnSd?-*fI1cV@CTBy3NJAa_`E&7amb03ronElc-=M7yH43zFh2(`sbIckxklzc zJ`xb0+OYN7-AivTueAU8^91FX3xytsGQR1V@+GdpCl_bI6O>}M-7haB+!u^8*H+%b z{4!6@{k1W`-5a98r$rPu%5H}*Xa7(qnEQNQ0D{?AFYl+kN7l6sw-YS8IdR%D_E}iH zbG8yjPcwfwyCBFbWcx#A$mY|&sj!2scauqB(>JVZnp)mc3-HrQ{N3q(kj|zai+pZB zBso=7_1aU=4M)=`RR7KR6R>H$bAFKZ$KeT8s8JLDTI`_$5|=r6uLX#(0rawYX+p-Y zfXo^dWG4~rUZ71f~F$#tOR#<$X)_d9CM=mQ3^q*vs*Q3VTa1@51ryg`#cwXZ2Z z8jM+Ps}Y4M4A*q>mb^rSs(pvUQXaRB*9P%FI*C^!ccT15+X#E==3)yIqc~XVm(}qT ziG^9)WY}0)lO+=t`dsS-#@(81Q3=G3bnH}r^urqu%HVpX!Z)v$gTur{K&@%F64?eQ zIVTHbyb!l%3Sp;3<@{XZU2Qx**3HTCrF9_q-OF2zv3nLKk+#88Vee=RSUC@O%4yq2 zs)FpP`E9pb=9IUJz8Ycw|LvW=QMgr9Qj)fs*RZ;x#>*bepR&ALq-XIz=32xQVX6v^ z_I-aSVp{EWMSMAJ_y%J|BD{fZvYrzq zsWE`q5PK~H6rVE;*hr+R^oP;qmA@YSI6U;>Mm1+c!QCJ8BD-pg?qn`D^qR%El~qe+ zN;+P^t2?3ITZLjyS}#x~oepPF$Sv3QFXVVw3If?x9-&-xn zFjG>n3^yXiP4>rn#CueRV;_tEI>n-*B4-2>6B8Z7`z$1{AeX(myo$^8({j_b5ble1 zK&OGFd-Har_B0+M#ski;v~P#_`z8h`fSwL~0|C?2M3E%3b~yb5aMiD*Bd?tH;8p)e zGCGOuA`z!zU`f^(OzRY;A{h-GkL7ZSTPJ+DdaruzTaZm4dF)%m} zCY`DxmdEZTN3JXtsN#M(9XL-qbQ(1!yOVRjw`1n51ppb%fqs~4po#ptw zy!_p3OlsYLmd)fF4zhdR0lX(|02;-KH4JC~%P0*0#tWX#o&G9h^&3YeQt3??Qrgfj zk~^X^ySH6&wOxJZc3fTYLt&2zcqA_F9waZsS~XKKBYOtxcPg>#|K*%UEL)!M)M2ci*J56 zf#Q1=e+`0dRT~Jj4E=JKad+Oh$ozlFRgQ6 zI9~+Ih^(5hS7vM-*s{>UB?K|j`7sKj7h1)LwH023C&gTOVtr83DJp~FnnETL_($^|m|X1;5F?QtY{IF-GP zj*jmA_v@9_K1GCluDNP%gN$(ArWHDN#`;U$SgZLK8RPgKi(Ls%JMc|Hu{8%NjsEue>27@UKiri6w4NH%r$0hry}KZc z1$wfNqL5|}2c+6s*bNa<%!!}KumywAFt8XWi58}3&+Xp%bAAce5Mxk4>ydw3MYnac z$IVXwom{&6r8P6J;Rm8m?JMhSib$|*5P{8HKJtM0X|^K({?Hrdhz6c}ce<3dGV46# z69ZS)$Rjl-3o4e>jC=?jCVy{rfrr@z**1Rkv_B$KBn#ri5Awa#0p+rz$-@0#ylWe( zVX~nKTbJ5lLklSxqWxk%dEwVY?>Z+39l7s|rU1^$@<;9q6K4#8Di(+3c1(23uh7+s5*3zgX@ z&$G{Ulb?elW|NAnGazr1`Mc z3O~Ru2(MUS|4XU1?V53PYny1iX{8=8W#M@(ZoE}3j6Z5ur zy+*RwnF1u)-X$Q1Ae4e5hgTBflgbe&E6Z zw-eC~lGyE9$@kpqn@4-#Jk5#c|Kt59g@Tfmm{q@IugAO(gfr`v;S< zdqMsc6Y5gf>&=ln1;5e;(0V3znF+Qgnh8>`1crbHNz;PqA4bbhjw6(5&E}^$2IN*n z1D9ok*Dpn&s2=X8+im`==KiemIbn-TrqzLPM$BDDs#s{`Cf6S(>ZcmCcDU!xSoT{M z775yiZ@(QDf!m|+&mSiQkUd01@yMUI^y?i_(Kt42k^U%8xDTngG1KS({|BROH@Ejc z9V^?5b7X3C)k2OK3Nyg>$;8!tCsVEad9RKV22f)8+oYON)fio}$kpl? zLSOWlk*j-hb8X4^O;3c?b)k&mXALL--F37`X3ed&GBwL5&H4D_bVVg_dc%d`NFcgl z^5K^2ZBKGKDFoRVdHc(_pWUcnltL=!e^F1A-E&p?s@>J3e>Sjd0)dkKq!A75{WIEo zi5FFev$Lk2#P3V2CJj+()P&B;VnVfF-cC8dCt0-N#z|Lz&PhH89pknkLXxIjE6V+g zRM&O3{4M+2x8D{obO{lmKYnzH84*f<KDC+2g&*XrwDe)p~Y zem4}*qU=#zLzKtS#sKLg@Qwu(C|;y|MET+ih9MAGe#@uyY)$p+^F^2wm+k<&)&4vl z;6gd4!34aJ_e8Rn8P^V2%6d0@%s6)-w)OMVE|Fj5f2Dz7≶ZGR08Cbr)tidUa|eb?4fyK*e+?xP$P<#RrxKv-s} zDoHAZ&o{QmV7U5SlHZNcz$;sfQ&ptxTt88~3e7QuzFuh{pQch-x|^OnJg}|iVYgrA z4%>QIS&!apX@wUS@De6Q>5+lI(ze{br#m0!ilj}wUegG5BJST_zCy!LfJeISjBQR8 zDKu9E8FT;aB{QK#C5`;fhTD3K_z_v{dfoRv)hz~KI?Hy6EPa3YLs74Xfaoe=tPW_I zwxJ;Sfj!3ZG+NSeSc_U!lkr{hM=?$?5cG=Q39=J%4u{q3?6$e@TbWhNLC5Dg~M&08zaIL3n+?VkZ#W4L&2a- zf}n*vtXq3g8+Mda9qcR^pp!6iQX51j^&apUTD?3?LFFR>)_C9{x8gsg^TS) z4NF8X7jameATdX-Z~%3cs5sy#7*>Fk)@$pneqmgY-z75l^^S$S-7FW{lAkLZ3d-p}E9V`;E!e3@ zG!M{kGRe~iwO*w$n*~64L7DHpE`0EF+7Q}^y7zl`V!7b!>0Ixd70Ot@s-RDYQI#iP3niXc3G75?CqqP6OFVg75=dY zOB9+p$~^xt-dD)ZMS>17j@x+r2bL&WUWno8MdS(wr4}q;SmGKwV`nvmTLxfuu0;DM zfe;Nb3s%=q{z4ct#ejTX5!>m3$G5NVP^z)S%O(5CNUhgGerFY6cb2?LH+;eROlLs% z#rAt2*GHftnW9!|*IV6%V^VTDfz&sroV;WSY!mg@ShQ=+Wdg(0j;6BjrP*++Pz~I7 zi{|LDoAT%0b$TYf{BQnOTj3Z)cd(?HbH#Xht@ApX8ml0$ADU4mr)K7_ewLY$5wW#p z)|&$_J3XLz6{zh>l2_>v@c!iN`7j&POU*o`+7=aUK}y>^{_W47Y{qyB5^p|fNnQJs zm|^+Odtzj=)t)HKd*Va{VGw*q^E`4OvR^80J8~!~c}UL*$Re}svmOtCWuBD#xx2Dr z$_6r=Jbc`trA^iaLcGhCo5t?O$QTtLnB*?N3Pq1UXLvl1Ftuy%(6mpNoykrwZ)uKXpR3iw=Ti2>faO<~p|n+}qJ%6ZZoIU&Ni*9!^fcTU|Z@ z2fX)Af6d_+i#X*>UHQ=6V55=zTe<;%ZHcXuMVNfRL?oMqYOK;UuKglvhTJlFRHJCJ zNg~v-?maQ9>QmQrCSw-{*A_FrWY_p6xnF7=wHQa03-AU{i#UhE zXo}e>5EJ35kPiCMKFXJ)*A^_$!9A4|c5mExL)+`!nUA5}8c*1@fH=K@XAxp@7`P9juJV{r#u`{YS(P)k-sPvt{9v*?PZT?@7yO(>|TaS!o zeH!JI{I;d>^#f3f!v2cQD$q@KE}cCSwM#FTevoII%kSCFGN*oI!6{N5YN|yp*7jGH zjD6_5Rom|Q!%>^(eM={;$%(0uc4pzPdYAF5hvAO%6^;X0hUH5aAeq26&iMYEtOME8 z{9mYZ`+)R$?WzjnCc|CeZD&n^X9$MV!oPx6;r@=O`&01%j4rLhcG(6S6#4hoJbaV! zmAPuOXMi>~Zk8 zb4zCG=9}*k$t;auvh<2JYiW`TXZ9O3gcJpLF;3r+Rdan7QWDvh-B4ncY#kmAYF&w= zT2M9FeOaqbPidF39d8`_>LJ2;ex62UYT4l&U+{d2!l7rLf3=lPPIxTjCFJ-pt7dxP zs~@k0KYfQbJWg3Av(N&Pd~Ov+wnwnM62E4^LfEcBhi(@Kc8zU5UvfNFw&wG*{Rv8S zn1Cg_E~~?{A5fCSceJxew58S;8+@mKM##&4z^Mt1*5TyTyVp^I`6?At?OFF68CWA9c3H&ZO;@rJ;&0nqBFtrI*JPM^QD}!2c^vBog1&cA5CmR z6D%Q!;=;m#i*YedW~D4AURaw4v%C`~6R)MWV>AcL9YN ze@%B8Q-~jf13Alpy4?%mX7_|mE*~9i?;V+ zC*)7m3du-JS+OR|QYnL$e`R5ffo%9?4mM8~#~-kY-{Yj;2spj*m{BuuT+d6_XH+PK zqa8eT!0tX)vAjSJUFDWxQ7gSDpt6*2xc&1BRqR;ylu#r^p`3du74y__4fI0(NYn ztdFlGv}5tZmtAoj8!zBEU+?LZ_in!&vNZ>LYVbRft$4}!M>;~0RPb89_WpjD6PA!V z*3OcR&LA7>&8)XT^~-#Y*Z^`__E=NbQC9A%v0H@tlwYzXR3mHe`A~(5X*eske+~iAYcHU+Z(OSW?D+$1;oq$6$D- zxybIfxH>g8FMMrUZgSK?sc4md)1WtdQ9-0cGAXj>LY|O!HROC-?FhH}V?fq^+T8wd z?0ddZo?2+${S?(r!MBu|ej7S|p8*{1^x{r`D_dOI5mOt1UPl3aSIpeUNf9}ruZv`_ z*Tz(@IRA=S|1P^}zxwhpcZ<@o>EJ>v<2~8MQW%ZH%7V8N z^(<#M`W406W9#e9)lW}aaj~GCvk#r=p9dcz6yP~Wuhy29(R?<3iC1lf9{j8*)S^O} zVPNHdSxtVlh?rwqzas16?-a^cIiNKpHj*1Zyi+pPJJaOyZ_1SrbwmX zh=E@Z^Z30goiBUJv8spcbBoOK`A%B=(#pcneejQ5YMT%KjL2F1HFLzT=Ty|C6*RUb ze#oo&UQXUp$Amm~>EluI%7sR+z6l!-Sbups$q!AjjP*Z^upp7)NH+5T_JrM5U~79D ztUm0j-pxS3jJf{T=@hvEPc5u}n36~{xq>R7;D>D1DRZMBx14s#))7%A{T=8V5t5FL zh^lz~#PxUbF3>YUT{U zS`Ut2KTi7Ji`=~S?e-QeD?NC{%(6#fviIp;ueSTXH!YQs6lpAsaRP22LXTw`?Iy$E z|7iUZ*)J1qo>Ou^T7^;JpH}Gmk~xr}sNeibN`_?xluxB>_CfL1091A83&ix$cIwN=QuB}$_QY)gr1dU^!7OD_6ee{F>T1po~xMF{LZlHJ(z*b zwca{kXW1rS=VHR_dzQDU!v5w0Z~qt<-QG(cjjxlanm;~i_FzC{e1b;eO4HnOWu9qF+`*{|shF9CCx^jPzO%;sy3}j|$JK~{;m_A6L7*MQpaU?9 zwJp|kV)_0uYZ*x@p?V}SyKafaXjQnfGUjt>AzPz+epF_oZcH(lk(uLB8do17pMdKh z$0sxpKT@J*MMmAck)`MTYp@zW1|gsY||!CJXp9d}0tz)+VFF zco^@jfcK&fx(fIqMg4rKmbW|J-;fb$KBAWwD%^2U>aKe8@q?lX3@1?olx3(Q@7}@(TqNiy;{?6W zrPDUo^0@jtH>Ilh4kFtuO(v$+SaI`U_IK`*gAnq>BpS5&l(p%v0bw9XD8bqi1I#0z z#SEvK5HI2 zhOLT2vBwzU+xyK}F^|o}ucFS9ay9IIjc>|n7nTxYdlw=w!$a)gt0sJ4DtWekhS!5F+>WE}UsId3pA&c!N#;f#nbVQ(K-#++_Uzne{Q5W!s+1&+qL7F1u0ir2Yu^!*U24vb~lu zuOvnQ;W($|clQzPq|nu*DU^Wg?n{? z_pumNQO>FDn%8``me6HuL(_!;z_B+CLlDxT;Jv1!FZxc9Wpto#VCnW^5qR{8Q=14y9XNaSL4nt4EJXAvpb5$jC*ea ziQydtycV(a&=^F_;=4)uCWTAhrNX0m`}5^SNvO6VBh2^uFkXM`$27qCn=4PLl+L|3 zXpPlbA2?Cg1d|Az3tL}%ST8xWTKim?7D)Gqv1?Xo(Co~YA391h?^^(489bbtS8IMb zNLAOe=BT&0yU3M11vb8Ag!LJ7FSyPYyp?sNRnXvWZwwj=XIno4pE6G=Qn>u>F}MFq*k7lMWl4svoDc=C`Y% zIHONd82LGxw*bdgfU+8VXl;S6R{bJWvRI&X4RjSmyTU5d>a4rgv?QaM(e4*gn)YN6 zDD%6^=-8j5xo?eMyN#(czdqmDo;WaUEt(kIfyRACU-j(O47Yp`5I6?0js0rui>|oq1=1d``pM8NgCtEedRbFjAhXKjnw&(wwU?4Iy zp!<)~&VfkDum9-p_82g0+Eo^=M>x1&JJ!`xw`f~HzIgP`aUQg}?kH)3Qz2UKMmL2N zQfYb3#`fv=&V?<*>TSh(1alnz-Oxmni(F(z5K+XqfzJ)@o!AHA zxr)qDV0JkDRDLzA{aGnle22-pLKik!Jo4W`E^~Bx1|rB2()m~hFf3*zvXo=$X!c;b0lOa7 z+10uxRN~`l>!T7rA`7dh8x+#u6)jeGA4WMf$b$HtJwKbA4xSMfY zb*nQ&-59+F*YS#$x-}`0L>N~{a#HF@R+F0GVBJ;k9V&hfL+D{>f(d;e)AUV=p&)Ct z5O%x=uhI*&reONNRP|enwGG4 z$E10D3bPT!5mXyaCG63B-sW@VI(0mlGj*`H$74A|t6l#T!sd7U`%9MjmS;OFESe(r zUUGX#ksy0>c}#GXJ^Nd(=!eOEL;qLGym1;SPd~vqBPA+#dGwcx_}>`ozq1M9`WCqS zkjFT+KSrjS`Q7Mub0rAegb*ZL=16(4tBd_%CBfPPy^O_yNEdZvk|COwGDLa$-U(<} zB^`*@gk`~_AAGw$S&_L3W@C6aOBKCPWa;~2pkLHedy?F#et@WWPgbwospF*oU)jHp#UaPI4~YAtfKGR%g)tAyq=`po9iuqZ%ZxjaxOHYj2+7);nQ?RYU3u-!45!)lS*%&- zxhs#?g0-CgCXV;TDE~IVg7p2Nw)$d`)E|n@82Uzd7L9MevZ^ z7GvznSi+s^I_C&2@v@|V!c#F^pysRczgDSwH-o!%#xY@031RWPmd|4e&^R!6wRv$` z`D$(4&}T4N*YWoi%!aCZ->LFx^n`I@As$#R>{1Sb8=u=6HkD5;nIL`Tw{ln1YSq|b zW^GEpDWmUlkgV9(*O0$3rYO?gaMb1u6>q_KA~ z1rwWho0iJ9;#tWZeEP?YbglbbE~*%T5mNE&KJ2JYR(1cLVKH0-4U5>uFi*lvBKU=* z*n5KI;uUN%VfWNSpF7nB-(xH}(VG@KpEK13w+;Iiud{rL($Z3g_H~Ri3Z00fZ!U=Q z^g05#Z&E#J8}r*{`{EMJDk^z5N%J`Z1J%{6@XJQEDT>2=N{-DodPP6?mb$_FM$$%u z4B_)7zX8`NlL7L*jOtk;!{n=#dh5C=?TcbPRs9k4R~O3kaiRo+OE|5+iPze1 z_g>ouUYwB|q27+YGMd?LF%I=Sc40e<3)*Jc^>buM2ji{hQn#?-RKB9V&EAv0$03c4SxiaMXM##@au+TrxC- zl4l!mf>#K6WPHd(M{9161}xpMQ_bCH@RSS zM$kNM*V^fbtk`=M35m}Arl!@;jY@iT zWww0^!&2X`JZ3HF`?zgktwx|%=C+U<^&j7|1U#rIh7XI6)CY?zQBo@DLoyLbr5X^4 zI&NONMb5imdN(mN#@lrJ7}RpMQ)Tw<{=%Cq`YG!nu(hB?i4+c;DLT~N{YUfD+NjFc z-e02*E%iG5l%*M#cYPM6!f!?BB*Dye^REex8`Z*WWc(yegPCV7f6DGWCm+tz)K89H z@=>=gDC#s&z;s&`#dc*+CXFsLeWhBJoU1O-Ia75yG+OlQ{_?_x!M~RsRu}TkbADo* z9U|;vMd^U=0QEM9ViaU6exUX1)Ay<(d3731AZpoFF_Q^VSjbVlZMHWP&{8q~ltKCN zhV|~U4zg7afx1{em8t9ia*9D)NCz=N|5xan?1$(iilNPlMLAng4hE=WvTd6tv zHt}`!(Ta+53r(L&yAv%1KN;Ul-Sj93JCx$%Mw?Csp5D9B7av(iv-%hFpH;$-lHW!z z;v~PI>XUEliZn-bo2B0@b2n{yPquFIm=22Wt$N10`Z4V}jAK4)!k((fhm*@cw0-*S zdoSEy*T&c>#*YGQYEKjBxcRMHR@$if`y1_zA$=E>PLq3pg#tISpRGjYiF$YWb*0uM zT@|SOtrpi&OGLE88+d>xCeU3fR;zhvMhl(GXW|=uAeS=_n`vEZf7?ltn7HzVU$Git zJ^j%_ec_x)@4s;nzrNuX^4$k*qrdoz4o0^3H)DSNRwZa7fF-e^^O@y z8EPI*%q_2*_;Q(a_x?Vbnp%)%m04S2I!wVOOe`9nemve~*7Z^R-Cqe_HMu4Bu6SUv z9X)X=oibhsP;(G|ZrD&KFF&yVdpo3ipOsotv88~7+w@|d*r@KmpQ{&n^6t4V49)$I zH!@MB+83ppG1HGt32nseOtSqY{{{~kWwtgA{WuK4cSZjj0|_I`nSUS85jp5nDET^! zC@L@VD8Zci(!oV3rR8n~X%!GEbw2mzN zGjZJhcy_*&0jJ8974JbZK= z`UX_-tFnBTm z|F+Axoa|Oq7`&Fv9;X0*l@pHWd;~wY+5`ON$WxjBVuQ88{1W!z-^tNez?5DykT49_ z9Zj5r(KZ& z^~Ks`PI2p?!029@!ksx0fBt-D!P|U z)^}*cqcoV(X2jawQZLpl%wUYjY}t|C7T|rDdjQe+bMO}5>zC_HD){&ED*}e^gcvL8r-&x>A4^~30|&r9Jz+{3RDcB=1O$YNy6@Iy5<3a+=gex!czj!3~+-=NRFJM zhwe7InpS|{_anxA;6=aUTi23sfc0V?#l}o&*orCXTD_~6d^NeutAtEFqdBYc3xWnTW^E)_|VpYYbZt7mdxufBsxXX74!yRgcSRP3Y<~;+rL& zlJ!8+HWE-F<>nKv%H!@&6(N3KCE3SITldSAJY>q*E(1Z*dLD4x)6yOZSfRU1 zI0UkjX9YF@o`wnLb&63|y|`xNu6Ew>4u6<fTC^vG$J$}m(<=bHEwbkgk zxJme+mdvV(gV%THV(y&qG&emzhF(gL1F@OT4U2}SUj9(Y;RPENJdREtp%Dp< zhIK(y+)Y$2_Az>g53wL=Q!Nu1>y^Ud3Q(U7heGM&CwgQ|a=`;oyWT?Em^o&Z_ngb+ z8g?EAJYt|C*PCC*!c&k;GUd#XgkbI(vASX}yr!p{-)yIi0uZSA`Y!WXhz+KUyp~nX zfD%S}Z1&WLX&13|j6NAL*nYo1pZz$p>y59^Sy_gQ*%fii9wQo7B3ev*RS8v%?tRDk z^(%YY1hhWh7Jq(yi7obo7xIXA%BAe~q8Rr2$nNQxPnNxX7^`leZluma$8r}Awd~r{ z$;D4vFs4Q=brjiZ%$?w=t(RvWo?yTjO^ez4sK2aG@>zBOW*&!@#o!A+CK}L;3)ju7 zj;EI?Q@!CemAP#4lk@~M{#=_a8#I|y?KarFSL&z|rTi}&kJlnohv*N>DDK2(qQ zYUCIw^w(F$sHYLbsBGBnmLW0UhgJDH(T(aSNaUl=D~WKKiCXOIUNuuDZ*$F}eXb^f zXG~W$=cPfj51~?(T_-V>T;)eDP&MhiS`N~-70fzkIP$_dUnw=t@%jsg+>we$&wzX% zLp*)>&(clu0}owNjtoPuUXp|Zt7{%{=G0~1lCCb-+eH`iCCazJhZryo> z)p&03TItmB?)vk(#SgUXHHOEBN^|=ID+f@`-e{xeLx)a1OC|5jq(2t772QLJ)3vv$ z#XU;5?ppm;2vkUfmG92hH|zQ@l^037D4$>QHByu#eDuh4eQ4gkw2nKCf3M>OH=WyW-Z^iOx^rOMutRxHv2RsRb}fmuTQ&PA`G~_le8C6xxV3rz{46#ApHVAp zt$25XY*g=s=(g&Xtu6=R=XNmM%}3J35vt=Mv@EmY#+-<>`p!7e zyOCO4)*Huean{>a=4wAsA>%#ZLY4iGkhC2b_j~_{=b4)#5f{n!_X#{?5%tT7B&GP1 zl9Q0ceT?T<|KPXSi3k{{kI4{8g0Sn{M(Vi=!Ecg5vjEZhdbL5MG_v$+Dq#3 z!BWBS-!j(Yb-c1;oIb9d($C)^FpI?Mh46v z@0#4TicHl+)D<2$+vB?2zQOZ6-2u~rJn+`zwk%S5DPlK&NNtlBhuJo@a#g~;&+}_o zZyR|}#ebOdPKw<3`qnO#w!arHyPjY-yH(XFt`%SdP*rhidrfa?!wtLd*Y$4Ln?-mA zCaG8HulHwmPsr=K!W8x+~{j6Zhnv?)~$Q;lGc7%Y8MbT~Z{ zi|wuptj9U&)(mmG@)Wk85-@^zo7a9-NK9}Rb<+&CrtdtbuXab{hEGf9v2RS80{0{R z%jDhp^xymE%ZIssN`Fynz7eD|V7|n1?p#Wwj6v2|l8%h4uv!jUk(j|`EoGN_N-}GO zdl{Zac!p<}nSRD&VQ^|78eRk^ z#|w(17+J>1%n{c}&&zYa-H6R`BqR@HqcxJRgCN(?YX#h`&beQrp_VYF!c*{tj@1Wx z<=BQE^mc)o@y18#SP#QJge;fQr=^6QAV)^Rdqxw;s!tB`ZBlb#ZtrTc4_0x)MXpLRR_8b~rCBaASdGY2#+Rekua~ht_1DJFPxDn?OTmKTgKYC4 zYr|Exg5Y#W!a}~WSb^P|GMK&4XVha5StUT7w>Q2yfUE<1Y@T46$H2C5n?n;nOQ40m zA1mP6D<2?<6N~oNa~wDJ>hy$I92|He;tGe3bHnp|%CG2|l=9rc4!o^o>Q1Gv?#}8> zF&(_1nm$`FJ@|y@wfB_PbBDK#=P=qOla>ZkbwoLO>!9@KDDzj9?@|}7g9#^$cw*<5 zV{;8mqCG=ULu+q}WIkWk(#F+a72GzAvZ^?p;}kT%gbwOQU{=&zCoxFLVx zqprAkKFjqAzAN8$y~k5xMeUb2L~X$DxNG@E{T1GE*L|}5mZuq0x%)t`ZjOh~IF85Q zk?k7Tc)mnZfPN6FjBf7c0XWZgSn``uw?mjSaqqSNMhCZ0WZX49iG0)Mk8k^Co`shv zO5Tu;8d)B@)v%Zw83vn2QaT0gg7w=NbXj3bcohGw*GXH)^EHQ*WzYmWD>W}L_(Sd| zF+QgXaIksZE85P;aYmbEI%IRpN=ztB?6ux>C0tJNP3ofk&;_B4;;kxs1Mrk{PEG9j znLf#kfp0u&NtxawjYwtZ_6{d0A(X3r%W#!jWx~c)RUfS07^?lyg1s-d^bdn^MiY0U zTDBlv$qrlNT9toJVzgzEU=btt%`@M0#kPbOCa)JavCy5_R(L`bZw3{gw$NI)FLy)O zuP?3WSbY)Z^UCo2@%|cm_g&FwG)FhIajDH#uevuHRjlGSnB(2--fp$tFtzT{A}KyN zQ9?Yf)WyUjY5j4Ozdx_RQ@qWBmDZ0%y+xy8%a#anh+zmGUOskAl;d zT$PgJWk7S#`?9fUw)3W92pytR*NTFfb2gCuNJ+dB{dnkP!X0Zb5be%<2&=0HEQ{6V zQq!*e380RE%0=`3Xq;lIy>IL}zagbJ*ahsMBF81CEQpzU!f2=~8@r=tXC^XV^hz;oYkwhhuk<8xM93zn^EWYj~-gc?F$tN;+eecQ-4wNaul3*haRi>@$25< zGwdC@jE?2aGKaKZU*m4^p7O*eeSFaUNJOh(Z{2Qq{2~z0`;M{pjY(&58nb)XR+TQi z#%`mW$iZOMG+1agJ6jx4v=Xl;7}3?T#b53_Ay__e#zfpJJ>Ev^&U$@IJUS6D;DoJS zIsb!shy?G=n3EkrdQw(xLE!`bqZNH#%rz}+m{6!bk`4G=l^G?ic|({_$W5y*X?V46 zS^0)XensZaNxGa!2~j;0&2h0YW1f2kEf$@km)RJ;r8?4-L+ zHLHE*cJ4QxV(k99tf-;TX?J_|rX~>f{6q?hd`+TF0A2!c!lRB4zDF z=PV?I5i3%IL>s%q9LiyfDSE|gTG%Yy1K&%66w(MY$UOVfpdj^(GRm_V<=_J1JhNi| zcC|miK)}${GE1I^H-G)-`kZ}niLs0{lY*= ztC~)Plvf6KdOaes1a@8-FRMI|?HrTZsfG4lfj1Fr1+27Z-i=qL&XeHd5#t%j6ZRqQ zWOBAjn*Gqv7bWt+9lboN+WHq_Bs2slSV{&ux zvo)6mXnTExp|)o0a-TEyoYTWQT3%53+I5Y}hi8&kH=lbqe2a^wUm@o^7Lajc`C)>s zP;>Bg8a4V`G`^S;Y82ch>%RE&s7s5;qJ=(LZTz5^uyDKE%zq5XEIN3(NPk-9*MNp3J61tM?OuhfAWlzh~yb(R~Gu|P_% zdZ_(N&yE-cqx+w?loMdO=!T?aW;ef?H@O?#;&LvS{S-CGF`g!SW?{s)2n0|h6uh`n zWKAmAS;4x~UFYfdZo7~pmz$>&^7FY)D1YFQ0O`1Mw?!QoP4KqS*NVTCH}JIaD(sgg z{3zHPuc}KJVol(ZD>;1IFlizQ{Zh>t^HgX;(TjW2vrMRAuCgfcSdPevO%>-rs@~%7 zN91_cOV~$BpN^l{?XaINY)(`31G!Ln0@;oW(mn-x#8KE|dY`;zYJD+p?# z_A5~$nm;B*qB54nEW#y^4q*0xz_2#QnpoI&_|7=Pb|J02Y6d4=4g}we6B_rg_APKI zWp%e4*%P?5(qC6x|7d3dFQ*jX?0#`F{tB?)rR%zzV!V_IarFv~8dK2(X-C$7fd>ya zMwK51P}soRdbsk=`2qUUj|r#I5UYy!JYP=PT@o|jkAg=Of6Nt=XK<*w4!zN6aRnNK zaykr_o1@8MG1eS6G{jk$!4MBcCZEw)w_h0>`(7T{U)8L2^Nd;jt#PMMB`Noki%O%- zcwz-&$B{~*T3-Bl%<*(ZhXN|`I z<#Ozd4-xCzNP?A(kK=+EP%`&XMl#BHajAD_&$D1dcod2hn#D73s-Js0xU5%9(30Hl%jr8RiKJ%vv=gtjvxatWN&s8=hK!e6e@H8H%)fO&sWa8#W z0}XU$^wedupu~0Sjxy%gQ#^GwN>7_)k1XLibN|PGhcmT z8pPd+xcby9$7={!5y+^3i%BM7%P#H)M392eH?0Zw54d*A7g}$|Gt{@^P>*95w)%J~ zDqz;(xbVv7B4a8CYXxyNy}wsi9QpVyqzZ4`I)(%mdU>T458Xk;aUAN z)gcZbgZv4{A+5Yy0`E_!noTIalcE(pIqBEMI9sA`3@AGx9LUDjww$hRtzR`UV9#oh z>;Oqt&r^}>vb+Ru_z7@Jp=UE^I(R0*16Yp{XRwboC&A^&U7y1*rYhb#Dc_>$xsa1~ z_E%|v?rSG`&p>U)G)?X(U|~iTFnOuPEBji7;;2XDMzw~x(Tv(#kMO&DAC@J}f+!Yq z;F@W21oeA0L~JMDZ#ZYo1x0f?L6%aEJ7?eZA#8Wt^{MxAF7z_+S-!pO)h-FT&GG}p zK&#uiQh9?Z2dH*v4l{@!qvYJ_m=K)roeh8Fj=t*wHW%0M_eV%srAx$NVE zNi|#~c`rk->1nuKcRVsS*BJrSw0J)*QV3vq?J;p!xA+igeo_MHI4Y`FmmSJmP?shs ziUp7MoCWp7{NR%|zi8W8#!@}i&uGQnJ>4?-b^lR~*<{rgk#(&2%)`v5=O4X*Rdntj zaf@AssnZm(8s|IapX|?D(gQ~V`F_WOKV7d zZl7P4U&bAe=c^!|USOI?MP&#%oqBJg`fRF%IZxFE!ikCMqmFW)vs*mdUUSrn9jYY) zqLQspW|OF0OLEXAs+J~A>&Oml5b~*ace0X+8XjEcJ<)Qll_NF?NDlGy(Y|!+Y z0$-{xPW-wvgI-ITR?@`YiJ9u7u7u^Ivl`UeSt=jGY1#O-!XY#(DR4$NAKcTryXYSe z!B+LoOXIg5>)#6ZV6)7R=%>4G5#{vYEwpQOdwKM!op!N+mcX;9uT}Q-R^KFsi7eDd zfUe2p?vxYfiexA~R`u1py=!X)*~@l2dZgFFa-@deiRs!r6t(G1-H~%6LFhYc`%HU~JjnmOp43ygeoWaus6+Hc&ElgjmuujkP*A=@v!kn^8 zPKAyY)=mTlurdP?ucFVikCGoTJ(QHYPr~1=srmM7rNoq3^MLPu6_X74y>K z7Yn>#{^^#hQdW%;V@zL`)=$ezSFCN1 z7|iesdQ`K1)UwrLV=y|cWWZX$P9h|$DNn84X7sr6kN&KC{KX~}c?Nq$aP1g1=HG2+ zqG_<{<;{Om(qQOAI!>eZgeLt|J6HZvi9oQiw4d)26|E7d*38#S_JLx>=DMRTcH%uGm=!9CJ+ov5GaSJ z6$8$gj!>Z)sb%Y|!|O>%Haj8?$c5Ik|4`am5z1`bWc+Pj;D+z11`kW(kGW{Gf0Szf zs#2?wJDci`p^OexRCj^f&;phC_4WZKsPZo`$T9;nFen4B-`eo>zl1Vu?u}JU5i~Bp zrw{=Z6>vlDrd!QjgBDQ&2||L2r+~=dAEjvT;1li&mTArPCI{C+fGw|9-Wmhy%x1s1 zmg+P`nqmO%^NojIS6L2TH$sIGpw^xVA9|1Ok^e=6-j*7~6tSsq5B+OB=zjr#>es~! zH0k1BxyA?|?(2eKFHzEXLaG=uu=1hk_Ui;-OP}NsWh)PV|7rQrYh@|)MQ{Lb*{O#~ zP&`WEfUYVZ+;y0Lo{{mXvFf!;O}DUkJ=fdKtL`>q-6?#~e6-kV`qIUvqH3*AKAsDw z*_Zv&0q!DHz}cnh(5IFrf4Si=;XnXq(uhW;(q1PY$o5|EE-a_)P&!8PC5@J?fQh0! zz})zNo|6-_s#f01$03WGMb(ZYhYG727ysb)cNf?PxI?H4li`l3Vhn!dwa(a;Y7t(( zqh!eQ=*#8cY54<&&13LbIcKUhZn%>Hnq8l6z0^|pl|3KQDCzzcCQ_ySw~Y+zkLVT? zwR%fd1dS(qj})pw>_UeNTrG574R>Ty+5gK-^njH>%+CjB1uCAH{|DlK!6u1Uw^lzu z%Uw_mt3w2N5w?#k z$a!jG!JumU#0AHp_oiZ;2Q6m%D&GF^i~Err8jaSyrE+9Vy8?sD#jNJPIp!bdwO9QR zS)w=NWDj&Wi=?c-=lpvho7!{EbW7|2qPUxC>li%H5$sdTK$`YS&RBfc@j|k{)1F7& z-G6+Hi5hT;w-QYPOhqGyCdi+NBaD^V;73_mZGfc(|VMnZy+JDYL3A+*nk z3=-tv{5LwW59q!2Juf<1QSo)w2q|K5N~+zOl1c<#|2R3ch@motY>+E|SjQ{GYkAQ+ zV7NMd$15V-vr(HIdBBK(+Yi$;_-#9Q8vr}q887GBK%KoCwWqR%f!eEs`~X-R_`s!b z$VawY9bCDJ0mIL8TJV!IB@73Bp7sv%cC!Y}BoMnOqAIL`ym0+jef30H$D7mxKn8BR`>~VxFJt?vs(koO@su6{qU;;$rGJR_l^BKJDboWg;I~@;VOD?& z4=`sEt>0r`K7`GMgUz}o*p*6+g< zNxy)^_`L4!K|D}&{@1k>y5ZNq8xpHPyWDB%IxV+Bg)@V4Fa0{GX?-N9m*Ayiz%)M z%uEjS8pZN@!0~^&jk_uC?jV4fe=b)1rS+eye`AJnuV482?-BC1D8K>-B{BU^1pQ@^ zTg?aCJaV8-{|AKpn<(DrKu7#(678>m=RY3vd*1Qyg8mqn1{j@wck1u?(O0grUsL^m zW*vVc^H#I@0feY=0oMIXFw?a^05JtJ1P439Bk(VT0ATx-LI6zv3j>-o>c3}(|1skI z4aNVAkb|Dx{1?i3e*z#ssMaqhp!D&tnN%Ka3I&{$zkhQEMdTglJr-3H@kwo)f z2s-GS6kTWYFf8qVN3ax`l;f0s`2_)hZT>UG z{_E(!bJf2CgyK$r8gY*T@_$(qQ0t(#(eeOm(t`$|==|qepwBh`jWyLvDSG^NnExIi z+LRT?0f781ivDj&f&Yb|1GfD&)ExlG-@NwkgW2Bz(aQy}^~W2TC{g`CO$_{tV1F~{ zAEoK8|4%5EVvzq>sr=0uePGK>37=fAZ~-h5n*r%hjR{bN}A|J%3!>0VT{NnCXQaOpo{1AG;t&A-5r%!#!3PJ7$=w%3bM zEcO3i|K9?D1)9?D<}j6wgo1j04bg0QQUT|x-u~>p3H*LKZTWDXKg^ZsBK*4sn`|2+_mA?>VA}p z3L5^5?+Tl%I@&LgBwHxwb!N|t!Kp>o8ZTR+aDz`<;%Vnzqa}>EugJ;w!+Cn+1uWFh z>HB=v{cRA|Gd(FhY~-;o>Xe^{c`?&r8BX2M^P`yr<4t7_>1{a-m{*P|(j!NbSZl1b?dv@d}=KAf#i<&hsnjVU5@8b&PHKDNNvC#=?8eed^ zo7CY8Zu1JkOKI4GqBWepAF->SL7(v=h>3Owv+v}`B~G^*H34kG1|0^XoLory(z>hK zdRc>y(mc%-xo)LC(?E~FFKEa;9%{@cop$P(|DsWZ(pJWjpYea{vQ9@%s2;OS7h^Ry zs)ST=WJl9f5heE4K0ERP$wBE81=l{({kMXW_% zZ|9RjWR6EbUwvrUx@c9+NsX8>I^D686+VXSV7sH#>zAX8X23?*POpUfxTH-NK|12?9{JQyi;Ffeoz1d7wuloh{Prtsh?VH-p$ZH0=-(M1w z@kN7s_;N{z(&3gwOyod&H!kVOCSlrzIBARk8zVsP@iJbY5W6}9l8I6aLLrz>r7@ff4qV}LE2#dFa?WBf?@WA(2(sHBfyq`u(gEMm_3emSn z46DCK4bMQnM%x#pY>8i)M9TvUiBy3mo%EeYCRB#Q!kFe zNOAi@;RgSq#>^rp{xvGIM>q|skAKM%1%Q|wC8}L8aI@U&K?9Gx&X!+1 zLOuYp=$QAjeJ>larj6*v?~x;af^W6E7GmTneU9G|+$9%!o*Zk`;b+5m6h&Sb#h6%Q ze!3w1d?CSYlPmVB-yI(5VkW4uwd6uf+3`M-jFTp!Zh59+%59V$Q&V)(}6ga zudIv82*FJYN6;f)Oqu{g5U`CALK(5SQvkdBa0I75k5f;>;07R@?z>es{(-ttWS=Si7xdC$Jbe_-)dmkM6z7X~2A3U;-Po z+Gb)P!HBZ8W2=07G^KAM)o=~;G!79~oCpaYEt6T6J}mL`?8~(IT`^|UL%zb7!}Kum z#L@R>k*kJF{%jp}mxfMf66k-rH-UsLp|pD`s7!1#?ohDvR`rsFmI7zv(6!lJYX+CH z>zFP@BB5#%pI&szaS6G5JQI8tArU)v)MZ+DM{YH=n>ur27(%}#HJjbR<>SdoQZo5x^-LOYeoLq2NxS~-tS{9Q=oPN%UJtVfa z8&#NqI4Y5EhWZ2_iP0O~@JpE82yE>=`yDFn&ld5M99iDCh65Xm3vH6hO8^RYQwxBJ znvw2o)44w#Z{M5I8+C8iUXsk+sw62e&(#`LIY`5MXlpc*CQ`zRi_M&v`t@<5xd!}`kDmbFUEj}Mkyo3U%ybEb05x01h`j|4lWA(p*R-#2Vj z&4n_XB%KmE`*zPJ*>eOe*h4%! z#GHb9r{VFZn8h*hY9b?~=Lk6(oJL9zv_Svu|wUY)kF%;t+0FN19VQ89(+| zR3h(0YnsBCM5CS6`lZFIs>15?!>TWdelJ?wFGAl$?+DBKbM6}#4*T}q!=Y2&WMGOK zQ0P#HwAOi?M&~u->}eEYZ_<)_Wp)?Fk%1Au)U)egxf|kOs0mD7CZnj7EL{VqVWH)6 z{M!EadsEU?#JP%H_vBmS=$(g|nSz~74U&}u@vr*&CST6z-)?J(`*1|`49ul{FfMr4 zOcczkV6T!5u&Ov;R9W0AkeiP~Ago^qS037v2pvQ$>%&CkCIJ(3CQ=73S-JQ{P=OHN z29wjWw`Xo)`{8)$K4-A(=qkv8=}?x{XRjRVnU{OAv4k6Au`R$8b*+0B5NAy5_1g$F zsCC07)G_L6PIBblRzs6gU&(^SB3_c*=WbYH^Gv6Pfuuk7!qI2$`3tT}T4Y2;#wm_+ zJHzMJ(js(0!eq(-)i%|H?dgc-zlSQw+*oSRIfZvXSjzEP&%o-lNW?2d=zP6HnhChY zmuuR)B$!Rvsw@7clt?R#GuL{fRiM#YV|t}9#!vd@=C#=Hmg*|=towI&DwMHb#!#f^a zpeYp7wLk{GCY|!N8mD+Fwr|SxL468vbC_57zRk8|V=~Rf=3j@ z*P2%BOC6C0anz_xd`D7Kv~JL+H_xioRJ z%FryZKtE&SMglM&+iykh-9CDF^j3E5QvfchZ-9riRIBCd3uR^|tL4`AtJmD4-o_qr zU>$xo$UG?bGwf%_b!HNhus_zGtHeu~xEvO?;KH2ds(0}aPJ*>iWSdly+5Y)@d#iMx z$>D*Dd#L5{$X?$lm+YGrjSN91d=6bR6_UtS@aLGMPRAv&H9t(;nNR1(Y8ulRzxT*q zy!sixOon=gG`UXNQ3V`6dGU@Z@PNO-dja?E0zaF2X)j!${q;?I_AD*!*$cq0v$T{i z+9SYQ=_jU7PMi*WB=_A^U`+OCjGooTA4zd4aa(TB+OU&xhm;DH9VxYEsE3 zCQ0Qf8nM`Y!sCT?moIg(8t2n}Bt>g?a-Xh*M3oAIB=60+m-Rg!oEwf&#cVZvGOpgh z8H>a5W?W`3CpEqKwAkg?Lch(FW)|0Wd>Zy6^}D-c^s1b59Z{QYSUdDcFSTF#xcgyD zzJZ?aAngl5p(YFRq=>gA}RwE0JS=C53G(4QLS3D^n^As*;eeP!=Z_|e13MOh&SOdELvtrfXfBsRElLcF1c$-)99%eQk z5nr7)ACz^4{&B$-;gX(Oy3VoDZ4flixC`G!;Lu4=!wr7nn-IdcR8Of-9jgK8aQ*(y zRLTXRw>u%HD}Y$a`?s176@0ZBnV+0U?vqexRz$aye570LTKpX$Z%y$@cacgZ79Z00 zWArYzu^SgR5biQ7v&%l{(Gcw)+Xf`I+}^y}DeR{wVEWa5Y|1;W%Xl8o4b{Y2*&n67 zK5`75-&{Y}i>8>#wKY!+86V!0Wc+T@N?H!;^Q&diG|y|`m{$l*hP(OY$8@a| zMHbejk?VF7yy+uQ@mWgH!v4N68j>ZwMZ6jbCxAu*( zNwZ+KA6pMRBP8t7__})IMY1xSMX7}+_KiMQ5QoC8cbAU=z1`ryHtu%e$6OubcU#mO zk$i(T$MbM9FLjfUazOWkUFj>wtWcQ#enef*%b%0&nS;0XQ3EiSX4#4B)WYeZky^Ew z+KM~#GEF+_0jih1wd094bq$i(?4jN-;hBX@n>)t+#6a2|t4AC^WRdI{%+vTx`Bz5g zU;3OK$9dc*3~z}tq?6R=qmUElXG%(DV$a;zbKFrIL-^-9jhbDQuQeFnzzoUUcqvip z%wx;BA&hGEaE-9y$3EvWyWrLzif4Sxr_}>B_d3H^yrbC&suQ6m35FYl?|nG4f&SF( z?u8Ry%s;1aR3=l3#dXOqoOVZfG(v~l+}iuTMExbd?s{iA%uHnlpQP$k!>TT)NCBcv zg19mHwNbo!4S9zyIab#%Zr-dJT$NaU#)tn@$ zQOCsdyFfMFRUeIeUpmkjMuf?+BH>uI098@cyFzH^pb1rpX@baIFmrVAK0$IURU4f; z>yLzJ>(2IdEShg~g&Ulxg!GjAEX-7nePrJ7eL8iDjMCF&E=G1wBIZgY!z?hDg?wb| z2Y()a1RB4TKy*>(b4!pC#L9|ydX^dvo{TMJuV`pgvfti)5XzK^{=e2;Zw%8Jzwru%+3L~BWiRs6KMOtF+-`$c;xwkvL)Q;{-> zUq^NGx)laO%<~{=8Y5=hBg`_cYCzw{u)y+q>Tqj3a(SkI>j$g$J(7IBbHC} zG3P!)rdwFc%;f}b7^6LN;j)|Tg%jaPPVrjpxT1F<3r(K3M!pF%4mQ1hCmr!<8IB)f zvsc4GRp+UwOupIOnfs*JUMkHm`|QE%M;f3Neqh!BeVDA-z*+$HQ7Q8gsAmFoHBd9NY>JKZvKKhWEOz!P;(PUg zgw~UWSI3RNq-;6D<;G36Pl^w&Jbd`%mB!U*Hr0F4Jfa+|A@?3UAOUkkkKcF}tt@>C z4HWUY_Ta%2BCD;Y?lfCrFiuT)#>PZ#dy5Eo?f>V0!83bb9?`9q2!!7t3B4mb6M9i# zu*;Mem@il)>|rh~CFuT@1^YXXuHoNs#Vqs&3FyCE5^|D+-ne3h9}9X1On5Jt@bAZn z2=VH<7fkwRivJk=*MQ&8{kO||!7qoe@qVD1YivqyJ3b9iS#$A9uV}#C-$$-B&Y@qd zwn}4{$Pvm&)`^q(?YsL&kYW$eH;(iH-05MFPu8JA`bIJ($t}EyH=4gLMYOGey)elX zBSD9UMe=I98VJm)>D&Fy8j3ld7UY}}PvgO{&!|_Fg!*i8Xy3sJr|xlo01JXKgn$@7 z1(8as&ZQIY5Bt%u)yv|jNU(Y9sJP!akNr+!``qW(0|HMiFt~#XzgclvQ_#D= zR6;<=sV2ong}v6&xYNC>V()GpweLTAX`A(EiYrYTOi2C`f~ashsBSB_c|rHgRRZ{B z1n$gSAd5!5Er{xCO<&&UwaMRws_%Hz1+C}ZWjU01?;@Z2@5xzZm}I&FnopwEpaUX<$L6tRjuzmH!@m?DxO)| z{-hGT>)mNKvM^Ayou(t~WxnyWHDLEmak0DWWqwo|<5jPYnH^{)y2cSffVSM-`a&qz zDZ~t`Yec=mtR|kWCkUH&m032Ke$!*Il=XoX+B7aiI7tem6v5ELj?U={$c5TOQz@_n z*I#%6?5Y`=D2cR|F$Y+ z#-+2v(#!!n+}nE6Z<*B}Jpn)Io)Ku~K_1*;8W**gcG@1ShVBSOX-ia|GZNfA+&R>~ zgG{d-JR)xHN1M3L)>*)Z;GPL4J}o*ZUHgFrL?|MvkFO5K%(R@n&_AM`v3uA5l5~Vj z*YVw|!I6cZmc!D$T!#vzf53U?O$oTqxGnHCLBYfP9qkT-VXA<-<1fjzm2=f)?oK|0 zI!QVqg?f@s=_4mYD^As~Y`!@a*%*sjVgM=I*60lr0)X`e;5_ms>rW$-B?d4&GiwV^ zhR~Ked|=%t-h#4dcYA&%^!5fb_-~)O69v-yrj97s(*~C%H%c=R4s_(p6kCv%Keu_p z3%{ouZ`%2JIUkHm?>y1x<>epPvZ8t7Ls)z>!~Z%$S-70%mw&yD|8AXH_rN-0WubRr zHINy>b3SxPqZ=Ckx3f-PO1w9+g*5~Xc15P$tVqOpg7nrA${pHlyksRdhLx^5y1e#E zrH8evmNhsoT^g*~blX9sGvUCN18!us%;nHWuk>MkS$aEjns)#C10)L}=O6LI%-d72 z`Du$!lU21}R_4;zzfNh(HPI#PpV6jWll!3dD0o*dm#5hqRSGx9c>HoIZ~KJZuVaxD zspDzYmR7hdC3OS^>9yynb2eAmu{VQ=E&)y$Oh9eRt@TxFP>Z?1p#LLvY-8$|be=f9 zRp;IOEK{^h)2qy4c}XThotL}$Y$8RC->b|V)(aEMxoRxvEBL?DKr)>73-$6>Wuh@S zl9E?daC=>>$I|-aL#R5o9*9_fMLa^zeAbHG{`T61YKx#k`0{XH)L4Y1pSCav@~wE< z#K`c0mv8?PNlBZ{w~!}mLpFwL1q|@)*aE72+|bZMzK+b2$3T-2E^T6hy*XJGjq$HP zoY@H!w9UDGMn4xalQd~mVX~iYJJ6RZr@cy2cDC+a)c(f6(b?sI)tT!S!1Y(ka2<^2 ze-Ic3$2Wi;WAX)t;I-)zIH}jFT-8NteUv+rmcu7`lqM`3|KCERC3AG+ol9X z5nyE57G)V0g}feAj3bb;P~+G`S*jcVR^!4a7QI}Gf=_-D=nFs3 zkcPf(eH=)0Ct>hac7FoQReN*O_KT|bkW8A4LV_~1y2SWH(SkN9)*bGE!R3`K*N%#m z9vEqvty(VQ7P~)nj~zGg{8(EkX)oes?uG|7jx~lGK$>-=A-P2q`8%)OXePNewNF$SLck7O;YR^Cs7c%|lXhC2`31NvX-&MBz9{_$7>FPC zdj|GqW_Cs3>M!5Kk0&zX1-+!Q`Vmk-tt3qO0Gy}9r{{vCLce&44LeFoe=W06~6yqf-2G>E9<3f~zKLVqvd%o}XT zZvRsP_U4_l6S6nQ-@^;__nPr*xJ-3+4Uq)p=Slx5^j|{ZWduY~>G^G@e~YLR^4r{J zwDb?H;+6IfwX!$=hhFhm`CmZ?^zMI&ozVYB=$tXc5}<-J|NWoAq@0m-7L6*mKo`$W z0c1$!jDLdZw@`mumgJT+VD__y==cr)Gq#lv#tZeo02AeZeEx&zVPV#zj+AWS^nW7e@lgb z;P24j!;&d2Xn_6qNEEM&VMp1nHNc>Dp#3u?-^_Y-kTxOzTwINO!o6;NF}Y&orld!MEV}-e z;@V4Zkf{caO8=TL-9H`Bao`G{p*u_95o_F1kuQ?0)FuQ`yN1~EJj}IH8axs)@cu`B z>LU)&0qLFd`5KPM_saf9)a|&sxb6Krd;INNfI3DQvPx=h?BCdciHzpw-|5Tl28{PN zEb*|n^&+ciTep5ADCIJ!{q3Ew578NY0?t{O%-dXdq8LSUt0u}xkt7#@?cXRSBE)u3 z+3~wJH4ZuBGsM444`3@z8dNaDD3~IuaOQ15tYGGE?xR(SmGoNv(V$&^CXtfn+=CVq zAYK{ps8!&*HtrvC5`vphF@PeUk!#j|M3*O>w*AY;~%`*!`+=8I5x3D z6nK(tt75CR+&6<;Axf&9giwpFl~ zU<_}vk&AG1I~#41CGM2%-Z*aEd`*+|OjraiCmD>1j@J|dHFDLM*qhcg@-c6B9GQHx z(cQDl?EhS>3`VY`VglDvMRA=gyNbZkj#e*M_6&)@Qvhfofoo@^5WU6OM}agdY7o3i zwFoxf=A|)PB_6=6fqTzb9k?`_@elSQfvW`%l$1C4uOym6e_B1?#gH?ZngQJBZ!m__ zfqO&)FUOMC)Xmo+l>v{h$0KaZ?boFxIJvTAFBIfb7oxzlI#`f*~ zGr6T{ zhj9gtG0CE?4#kKOW{WOSi_g0Mp%@QnaC*=Zz28=Ct1-HAR6nZdFlco|D|(ei=^rBj z&A5|Qkd?q?N6=n0vCPn}GWz2Cdj7|l^`?9O7FRx)K2+P|T+JyRVa}|+E{lC_xeZiw zr|B81N48&keMd0gdn#i?knciocA3?R9yMzm7sSQ)&L0y1Q0;?cR4(8S7!l=ObS zRn6(m;buZue2=c$C5*C4L)&(BO<^7D4`!n1O!>M`w$sZ3=T**R4Nwgy;(l)j0xshy z=+-+8*K+Z(M`7A~sq~#vo+h5=WTtjdEU-+JKYSVP$(Fp0B!#kY)MNYp$0_5)WYxkee=#=Fj_i#%$ZI^c|H$ns- z{hLwcfa-EoLv(8UEu{ysgru)8x+~?WJsaTek-UK^`<|F&uAg?Zd3Cr#D7!QFU=QJs z^FCUm6Feh@4T+8_l^MTB@y(hlf{}vgojSN8^aBCLbAgvp{LeU`Lnz${I@nK46Y5Ln zRnFsYX5je|ra-=K!be(Q6X>a z&H)g;{IUol)KhHU_UG%D^Kji85wh>Ov8}gC{O4WQ0CH3FoOC1C6er&5-n}tQbp8ER zRtnjZX!`JMwv{|+u-6qhvpVjWd}(O=4&?x$e)4{QtI_zUpY*&zXJ)Y>+tI&jwtC#3 zpy1yiFgD?)lsBF;T@aU9z?X z$XwLS==MrgCo0Swn&97FNGV(&YTMZUT@(MM9w0w(5d{Env6_#~V+JDU%k=c23oBPO zs13I6di}Wa2jrxnL2BiUYMbvr4o{>zB(w4#bbC$6;gS*fNHqh}^C84+Y6Xu}JM%J) zREo$UmW}=MYGJH1uW0@}hmXSO9qH!z>q?Yd@mf&9lH7!Q$oPaRX$5u4 znSYqEd~gyGj8>#sa&`Y<)X&}pAVf=R1XqskXLF_9nX>der=i_1)Qj>fU0X9YSbI>F&#tf8P~o>D030qQ)(Y0?|U#?qzd94VNIg}J zfI2rGv|u4w(u(AaT8UPcz&&*W+?? zK4VNuGxcksl{^mi37HEA2lav-XiQavZ#)4e%`{=8ZMtB*ajw|VRdm5%_Y1`ti2=x1 z5t>kmnF=@t3~{SI7_jRp2?3ZKLR2$Dux{gl2aG<`?U z|1`dq+QFJ)W5xW-ERZmO?hT*^yi0Q43@KjH%vdN&_Uer+#-4o7c{HF*SOxYv3MM=w z`=yqxa8X5oG74LF0aU--$j^%!h+cWzQ9AoYosQ9-dkJ)!8Gn}wKuol}+}lbl>vkDd z2c%#?*uex7WY!v3{;jlju60DZVy-5v+oKt{<`$J*Tnpc;XyU$DO!4|?4$?6 zoLQm)g5PTW3;}R>_HSthM6fdM9y~O62!Z+pzX||Cr2fHJy^EHuUIQFvksy6!VE&~eSL!q~8O= z*Sm2oPcuU#G_)gpq3uEW7qls?62Nv6l^w8<;AFPu#*>k;fFJojOX+i!0p_9Z3)SXreniO#p!1#8OJR&ArCWdP7CU%9F>gk~==FkXA{+qZs! z2)(1uQQInx9J08-EU^g~mfm{Tas4>V$|X|aw>e4)(qG@1IH&hbw_6lQWWTMOkwaSY z{Rg33M?^8|%l98M6w(&Y8y0st6HQsyy_R^MKy(0>4%F9^2CU6;{@B}c#XPU^(PRNY zcheHhU%GcFAs9hdUNUIUOd0cz;1wyE`t7>@{%WI~lX*W_UpQ zz91NhsSwj4f(CM2#)wB3q!>LT(DmT>)+xJsvyJTj?>1g~HkDoHL6u{lJhRi!znaAx zR|yABCKi~XZK8xabGjm*N!)f{&n$iaDg*!qXg*Gs?;U&>G;!*xbo+JlujO%GEWsW+ zOdtY_V9k=(9hb!?L_iq6M+>?m?=|ionO&@1{BRN9e9|r;=r$CE%=4v~a{`1~fnEG= zDt8YcD}m^f;L5N1bLd8N^`>=D%o+metOG7RzjXAnFg#m$WjoE?wZNtrz3hrM<<0SXaX&W!`I$J)GK*Jh1Ok$^WBKsVqIzc@6%xe zsgBqZ^WWZmPxZ=Vi610;W?L}XTmTy}I^a_sPb!DZtLj%E*;r~#%c2opV?4DKZsKW# zL6NLK9dm?0uSFI6*_sK%4&PZyAMMG>`wCW!LwOpj2rw|5v?rpK%q?BOSZ&zf4P8{- zHvWR-U&<3GwAv7C?@T(Dj}{a`?&< zb1)f0%p_gH>H%cS#7HJbj+*?QUR(Y!pi>Lz>fU9_$@S-##Iw8YG1aNsn~#FkuXoGz z=X_TdBwCS>Zsuub!0v8}w7#<(vF@qL3Y2hgD_S9xVf>qs1dH5kUXZ*pNv$3dmEYCo0otf{eXUlXsgo5E#P`-mfTit5Pi`}0S%c;Sb8p0Azy+g!_ccXcrC z^@`e+1PO#&+Y9I6zzu^IP2aAO@S+}2xIS*H88V9zg0j-Gz zbi2f6?QvZ94@h^zV|y)K*IPsZTO&R1Mzm-jj+1iBz6w-v@Cc^tjMe+n%(lERvvX5?0Z`&3GwNa}FaySp~Tw!5B z^32v?D08nZ7{oZem-;Tbeiia!;#ZkNg1gB_d=)&_U#YM|kZwBtDZ7j7m2*t_T)X;& zX!g&8BQLXoh9x(aG-agR1=v1a8uP&*Ka-2E&X8RLfCIoV|I9u&vKncgr^DX zY`|932XsIQ?o35gk($Z9i}teQZd7+czgN zrAB3*KQcN))DX<(pZL&OE#}SBJgP>pu|-u5q=JUA2*EC%+jcN`RiE)ahocxr+twt% zFecGU*!#0E{ocO%G;YmqX=9p8{zFtZtq*T0$DQ`NQ48a)k~B2Zuqt5N-J~j+tNCPo zCl<&)Pj$Rc*qU=?K^RYq>38?=O=erIV4zBIBxIH~3pVP@ddWMJLV4Bdtu%D-mWQz` z8UsYn>v45tI|p))&5jQ;Pu>_jKmTjJVi*~C??q79&w^t{mFig>9WVU+p}0{V<+N!~ ztrzTx;=SXc7Q)M122qEf4~7M6T*J6@uth#{k2BDZj^tZDF7;%7dB$(xS>H8MkGUWt z6PO7%%8|^*DPji3<~CbpCen>V3iR70tYJN9ej zgCTe-R|=K8m7Gys_#-f?H@Zmp>9E6+xLLmE^4*8bc6}c|Typfj^CfmyktN+Fz1n4L z)kW}RH~%|{+vnsr?lWHEKNtmlB)JNJwAl7>yI@sK>OtaLIUu!a@e|#(9lBJ$te7jU zOS|S6b5|)?+s(Cc>b&3fGdd$!6Q0Vg5DXx+19pu7F8eT;(BUvb7*oE!MZxgPkm>R1 zeTa!H$b}^fn*;Nn72xP|doj z%hjoEQQs@h!yuC^;#|I$*YoA__z1@>U#{O*cwSxaxqMr3plF1r-rHKqVcBIUAs&pf z_ltdR$hQygSmT$td8o|aQaM|7)NC%{yZGX| z=rM}~7dRIFs3>NU$=gV~tMvj>IOxFS;ZhQG+;pEse21LONlxdQgZp<}?nT4uwWldj z>eZAwi(2!I-c`8T1)?n12~uH~QH!A_X>IcBcdSq==G^@JaScaA+=IFn!gW;}%)E8E zv=KYLa~G^NEvh0-tBeB;;&`^vwm)9IbKb`hQ zv#mrK6+D@=N7Q+jQm*8#LhxY_<>a+0?4F!Wf6OZv>&(6n(ocZ=Xt>@Bd9znlGecgB z@Y}YW28!(H!CDXfFwkR0+3=U>bF3`Jn~9TAj16_e$d;>+$1^&(BGbUaX0dqDV@AkV zTlA0b2p9NZOecrSFX=L`!fL0(;)It)mIemZqSb4TJU>m3jl}{2i^3^tj2;j%FtdMQ zCC?mpX7YHXh9uq2%bk_cK{d8VQbK}h}!~NKyvs@Q*OrDK2yTskZ~7S zmi0^TN@i;FA~$7ZnmT>Qx@CX2-*uTKdzPbu8U{rtmC?*gt91v9^C`s%Lz>Tl9UiyJ z#Cx)*y&~?NUO@>(NkWxn7x3N6;*$xF{+Z5n$>41YCLL>~br?K4(9!t&x%ve0f|}

OgkN>Q6asVi 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

l6RLSeG*7gVi z({j7*h5{fJh@!Se;aWvM|0vr)?fC81wveuABwrZR@VqvGs@iNgxwkzkJ)QGrw#=fH zmxU!;*lxIe*C>XTtc;k;GU;H_+MdY=nA=TUhZxHmt}d1h=0aPmGA81a_`0|BHU z-2jWLSm`<5Oy&?CUYR}#x}RXCU^dL4eXtfFwpbY7jut#!q$+g#yUe$?RIbSmKP-bdJRWhmiPIDMmp84Q?<*{AeDXH=s=t0+nk%9A5 zZ?JYS5)3Wm#c#SQ;Xt5-WD6>ny@C?~HTzXPeSTOc^`!anTlHl`z2Vm$KfL{g1w2&* z$JeLz(AhwhbS-Y*o=+BFlz0(S`o4_o%_C;;l_OALXfGN$)Ur_5(Jer_*=I+gCS z{YpOm3QUfsAEX)2Gp=d4Sg>Iobo6%h*^fzXnMoT5&Oj@BhpBtwTDrtVWclP@y6(vl zkr$Y&yzE71x6q>oqUGs!_BURIRBevOo4iU!?7oM+V^F{k+dcp|)11qGr!-F@EPYeA z(q(QWOJcW|S@)#-wUym$y|o*lH954i;yUuV-YmtcHC^k5#Vs<;A6Ta=7k$!C`kj{5 zb)cwhn7}h!5ND9I9Y;M0hEEF@;gA{`*NY&q?VOnHO`vZv)w6sd=Q&o2%U(%)Wgu4j ziV7Q{nCN_YybV#%@8l^l-?S1|Z8DG9Q|>ofhSS%*Eiw0v3PryWA~ZT|hOZ&)Pz)#? zAxAwHNYyBmBe4GXrVMm*KE}MSr}qpS5bl+J*yh^1>S8a0Nf82NZJrseI8}y@UNtLM zWt7Sq$;66rGA^endDu1W#(vHkk4lD8x1B!b&#(h?WivmlNru5=SE{YEhg4Tgb#3Kh zf(E_SvJB}tq3aJLNS%XD^aTX!tU{kQbz~Jv+m6VwHjuU@oV%&pP%oQ|?uHEj+400|NG?I#u&zL-Tt+0eZ`U`z9eSKppw| zVbs)S09k_>^YlR4T5lELs!_FFVKA*}q+aC6;aI=R$I zzOPS$oYNi0LuSCQaEL>o>m& z`g7y-(OJKIS9-@3Qqst!GeymGiUut#(Fm%jYwN*q{FrPNr($C_;r8WOp-MC(=DAi) zxNcLhcdVpaMOgKR$K7r*=bIc20B!4h%qlhOKGro{G+N4~^+s4GEcekbelbQEJG}?R zvjS+==y7nay`NmJnpT0>^PQgM?Ib)Cshv3I8G%vXRq2(R)4FtB)JjN0lh4-l+X!O` zl^An{we1tz*6m9AX0)<020{8xn8NGZ5r#xYyvEP+*40h5XCsHfR}B*{{jkys6n1 z7Hx^nHugk47vwy_2@$D1Z&Sx-SbnYPlhpJEGL;OXQ_Qe)oa6?A0T{*K2&4h80|XkQ z`Z+18T`Hxe7&RQFlAyY18sZ}Law2Qnj_}>D=~7$am*?-NCMBi$J6d zP-Wm|7F1RB*~X`?;IoZhov&J*hF68jWCi|ad0p3&#?dFuOm&#%eY8M2NqdYypN+@S z8qFA`sDANobc(Qg zH5llBT8DKs&EX+#0qHL@XAX~mcolt`ptpw*@?-0d-}L+&e1w?qvElw$z*{ez?$Kdv zo<_SmK0mz0Bv6gU!B}Wmsy}GJUUVOtX1r^eQyr!luFvhnlwf;06kHl>CPzs@-PAF} zu~WQpC_AxjNk8?zMc=tr_3pXn;9;6rebXHn#>3d$ zROc0E_lRJy#&)U)Rd{^k%){~g`V;k^;nKCN#M?k0Y((^+f1hSp$@yelV^f#*`Kr`* zJ8PST_?6ordfAKeQ|Rs>>yob&U9G*fX;s{38>u9k-*JnR%g6%KyO>nXs_hE*Rnw1E zo_#P7y4nk>PR~d~LMUE2yWEA4NSvMp*nCF};ruyPa*d8?Lqap)PHvdL5{V<`gs-wf zk{b)nnsT?HW9eWCuAE>PU}`+x7_0b&R)|X;e7xNjc011Iw^*9AtpEqAgf8Q-O9h?- zM{>DFGjVoKTIOSJgYB)CG{~XT9CkHz^4-NuR(5Iudv88yi+UN}N5BDgJjo8is-kZu z)w|cgV9O16$&24xfAqlP+Pz*5&{C1*OPwmmtMj+@#C7O-hq0gq1oyK>CiM1Tz_LyI zCR6Y25a#nSzw4D3#TnN~6@Gfwz7lV1{m8Z{H)v5QxXQ(e@sU%CZacGk{5nuscNzl| zmfj6xY>qSYw@(*!KO)g@O>rT(fW~NiZ@xaEJ|XaTxKq^9K-StP1M2R zF@Ip?Iv9v8k|y-oaFTfP$&6G*6+Hgfh7UN=022Z!x0?~wS2d;24V-@E<%>86@w%)j zZ%kJ*^9k@rdyf}qG1Y#{69z(!!12X?3eh*CARZPVh`d+_K1Ow%s7pXQ_r4rj&~^ki z?mGcBY5lPNq*G~0EB+WUoLgF52&MLYO!mb5>ilo57D<$$8f!XR4gZ+z>+2FPA!}-0;C&YY4BJWi*NO<2o9itG1;{^m9FyJqTlZFRtn$$`tjb6y>_z20b zdK}ai9gf9@DAau42yDYR&6I9EbHWYCH&{=f`QDy;0!zrIHwVXGfIn%DLkV*r6A+;6 zzHfw_Co;~0(8v5sI{Rf{Z+o+d-#&u&jF;>B$|Fr%6@OkjnmBwthUV#0=)~(gQU?M7 zDJNOXb*(awErQ@d5{K;#1Rz6R|FyOFW#qP{#h?a|@7ER$Er>ev0Axjy0oYHVj2^rK zW$H>q#D;@nM2(o5k@hkYr+0m0unZW9Y$iKOtxn4xuw=8a#gZV#8Wq7U%U}V%tgA~o zLcC=R5XeTv|2ry101)q=4_yGXm@!Rs(LD!+gzCi%et54S_$m$%7$|;=w}x5Or~E7S zKEHue_0JAq=YrzLKFf=9nu6oA3qJ5>x9x$pK|p}J$O4@Z!nv?J1Lo@Af=RKGK72b3 zL>cyn_vR=y)|d?o!Hoim8_0|5n<`*!-dN06O&T2xh}_-oOzxFe6p6~} z$1ri!G1bC1*3o>C4kS_PM5im8O&C}+2CQHViu(F44OFv(!PkO!-v!`s#KyA#uhR8W zuksYNeg_sw24cU+l;4b`>+qt~W<5_!VNAEymA!ZOt!(7U$$XKrqex7Om#&+m1Rb`& zm*`KB4x0-nS(-`k@Y|ffqyGo}PXHD}XA3f#=p=rce$@kRn!YIrO`;D2QVJ@%y#lZ` z*idSLtTPwzjqj}r0fBC*5d_jT!8jV;x=yQmKBh%wTVVZB%ciEjCTo5*G%7eV&C04@ z{~cxXEkWQP0AA92|FR@KSG;y<_FSg{RfdGAIdm%I9W3hl`Fl{2HFs!2ItzpZ8kl%J!JAU*ougP?u_gaRt< z5F#J*Me|qLTK60{@c_kyCX2AH!NG-^7tAa{ASC#R;l&65mP)vBe}wAahp83#*|_4e zS-4ja8KfWebi~7&r!S%7r>3SNf0V|bQG59pY5O~4@(isF7{xs1dG>Ky&`Sl7^sd?2 z^q66s?zC|dXnKk$1^U1 zan#FNJQV!BrK>nHO=jirziFk@1G?ZU3`ae)5fOYD~=Kq03;e~rt-j>CiFoSHC` zwq+pAu6-(dG0Volzv+daf$7_2ogARrp8YIz{K*~OLH6`J$De~g<%s~h(>;sS z1cGuguQgMNR;I>T^Z?7{zf=Y$(n>18ItY4X3W(L)8VW6Mk^~_`X8LAwquWMs6%@cQ zP#KRv&hGY{ugXe-IhAxBaQwt+&pf(*>rgh6*6+G2Nd`Uka5aT_4c_RIH5p#1qTRmy zA9XW2f&{n9Nuy3-PW_NNcYDg_gUS)^E*|FQdd%vmf&!UcPPEf&$@lNnCU5|ic>C@x z-6Szwu;qc2k2NLUrs_9Nb8%>ox_YzeCZ?u_D?TLH011FMkh1LTk2Aras3lMnqk0!e zmR9Ll4!7^9L|_8tDM3q=OJ~Net35ocy>Da?Vj6~658ttG)5WehBsP7jQbBKJeZVKJ zto6=|z;s!;fM>aLNJjb)pf|t>%JAZ;heqVU+`6Ix=gq@6Ihux^5=SWim@|-ng?=!i zv7o*6kz?%b)U~L<7jM4z-rMU$Kw`SbCe?Cre23Wp8UPlW-iDsjZasUSz5oNNK~1&W z=NYNPaH<|y6H^B;QRM<9BQ|d68b0F4+;$_KrVBtqt)~#4;ms3P+`onAr9V5$B#`%- zJD!>1cE$C*>iHZk3cQHT7^G2!L-xgVoMq9wk^e<{d`G)VL#^xAraFNjqg%xSSG4Hp zB^Ae@L*;^BD1*X$TB)|K*m$@BmcVa_UBu=&t&j{i^V6rdTV8Hn7$R* zpY$I?d<_5#06uCN20G+$Fn_xqt}%3{pI0;e03z5x z0u5ZG|BJZ?sK5jWV3?`)jW7IDO7u(66Oy569!4#5_MpUMvrtnZW;M^JdaQ-SQASe}kEQ-OEEQ|Q~CI-s!d zU!pNdZbWFr;uk+oolV{P-Hh)jtz*3H7_eaAGsEqu;h-V|F@Z4w6tA{gHFl8o>7XDx zM)C&($M%F>0dR#@tk@w;o%_MR_y<@`VMgo`YO=4gr*s*^*u#zJF?6+h5Tj=2Y3GeL=iBxZg{7-z`e*JH3!`Nt~`-{jT-Jh z-t8KWQ6Tl?Z}R-V;S3pYmEaWn8~kv8(7&JuoUB{m?;sX9!V%62f5QRq18z+G1GDh3 z)!nWERq+S@TkQXb68|rFq#5{wrvKUlfM$Wuw|Bnw|3G}o=6_KCrm5lbeaMpcsi1FZ zm4rp$6K$R_k#dnI_tB9N2fKhj0V3hRoy`teF- zlt(s7M}T(>%)98o2x#pBR+tCwVqt|w>vk-6!2So6@NpZwmG}#2_^mH$$lp-^j}QLW zZ}zmxmB7dH7bSYcK`Vro|HS;i@bE7vk&R{%6##tQL9B3~`2X_YojM$ZyLdoks}i|G z3Vejb1fvIklSXx(=$1ftD054w{}LY#_CKJ6cX$W~;|_aqO8*VzEx+A;00;XI?*F{e z)5>{^l)o6zeBlqF{<^vwt3 zip57b?*DHOLjGU{-oQWV3r%45oYw`#&7mLc;E7scONMg%)?8oS4Q%&jwXK%$21apn zdIPh*(7$Fr#@fqJ$*(IC_ES!=bNm8Ort|Q1AR0b!4GU>{+_IlMaNU0TzJF3;Vj_Ao zFp4K2PIh+o2Y>hz-r!FQL^k-F_nZq9hfA#iF zK;%P!3r^3ys`FhWq*IP+pqa7ph5xYMWtHFM4QUZDoc|wRYrQ>(<%#UQ&@23dIi!a( zA5^{5SNN}bzHIgX4)+W@&$}!y?p?Lg-x&60KGYk%6-P? za$?-h=$KC31tk0Y{GC$I!zNLV@r>%R>%YdVt}~d_#-EQbH<18+mmLa%*Y`NXN`+I1 z4havpPp$MT$e9}#Is7W}Z2B+Ih>Wh+4$30JuNPQcd^seyq3%}-{8k_Qemc#GIU+i2 zd+LA#O_q+)=sRMXzUUvRq)Xhm{Awu`7FJ53sP~af&)*Gix%LRJ3gG3UL?cS$$^Btl z+oUx3M7VyFZa#*g+MID zV^>%=dLdW3S5^qsHXqN_SI!UmHc=7xAn47zzu&oug3NDUv_{%nIxL3?>UWouENFU% zuEyh-fgJY*ZH1Ix5^a8ueU9GSHP_KI7g{hpm!8j<6w0XjeN{57f2-@lpv$72dajA) zFujYPRjqKdkm*oIOy7nirL;bSWA5ua>f1bAV zoO0Lu1X;?(A3tQ6^F^7T=&a0(ub|yjnzexFDKJT+>~NJ*@|=Ls+;`fx%4IPlbH*R3XHm$Mph_B^H&%z z?d5Jy?8_=T5@Q4p<;vxeBa1mbze-A18gppHpySu3M+6;P4C}lFku{5@FA~gW+Nq9R zbWWf(M-a2^*ZLbdCvnupij$4DMS%xooZxr^UPDMWYK;`PFIC&gb*b=-xUZv3F z`}DHqWag6cy0EdhC11K>#AL?+jk1iz%=b;Z)3p|t>B({>5fZlAh_HS>CIeIr)VJPW zF1UTR)e}*dM`gBnS-uY#1BS@+@kAx0&^g~)#rZIzk2akM^NPeCqeY*-mQ`=sHEVG7 zNpYf2f5cIEnP%H_S~{y+I@{(YSbaWbmcB4&XZ8#e-Ef9yr~5)L8OLg{w~Uf(Fvi8B zaZAbce9Fmz;o1$K-|xXsckKkBiDK~iZdoUXYf4qms*WAo#>*(2YVswKddACB(fEmI za52&_lx4%jCP~SLfBvhNU4h-0Ca?p>Mw^MG6a?uv+62WZ2S|EUoq`1*h+;gL zX0wTAId87W&9rgXyrCj}B8I-MG+Z^wUTN%fWF+Ghy?M?o{X81H4y`EMwkG3`-X7xj zGQLO^onU3=ED^@^)I=dsaktM^EP`@S=ucTE)?OK*K2Znve;^~G{hTm8`v+dk77_oX_P znfd4Wy$LAu+u|wDoxvN?e37=&WZoZbYj>P((x9uSrfKDn^%V(^7a&~mi!veY_CUUX z8p@1AXzIr%KkmTSMZ=T6kqSwZqGiG>23>P6YMRe1CtKoU+Zr(9FAsQatEz@9a=H&y z#_Scwb`@Xn9<(EPa1POy7e%T2?7rK4G^U{2$OY3ZGu<+~>{A1g$C+7zOBTbodpLeM z!?xmMPh&8vYG881;fA3c`fsXC_X2Bt8X1B5w1e$KBo(Pm><63J`M+jD+^cU3{NpKiz=a&u50SA4HO#%x%47$47>!9$0GH- zXS{sv94E3a6Ey#}JMjFP==@rrcP?LCWaF4{ZFuhhdjM)T;JUEkSOpX}VUKKUJ52SJ z?@TZvaa&oa(;2E951;bZZo8Y$>-KM%ywOMg5>N8fmlqZz^nEyb`HW*)_NV|qWI0kc z!!32K3YU8OH3P>y*x^TW!lLOCawfK>z$c!j`R?QPigU@F^!!QjVeCva(~N5uTSxYK z9}H98OZ(?T<2LStJ3gT6=;th!0$Q%i3kfyLZ+|!j3s*JmqW-S?WU}+o$F0A-P9sNX zt$IEMC}k}yc2!QtUwLXTd{L<-%xxw1x=pl;rCd9{myb}`5gj+aAf24zk)_bptHC~#j_Jf+ z>5P0jnl1Kk57yD3ZB2w3O^N;#$(D z5e&h)^1^7w3@NQTR>loJ#Vg5^JIRd~-KtzWR0hXY41-8hTtun~t>$J>=P62I->=0W z#K=~>{Qe{i4Ec$l?l&DfbX7D+QdP$tvqOnoh1E4H@s5pYv#}_sU0?TL$6h(9!s<>m zqw=mZI!X~tGNEN4L5|gh^amR7nBgaWXOm-5YO?F&lyc0Q(t%V&I7}V1Z@sF3Uv61%N zf(=Eh=#bihtpiOZn+D`S6z4fAcX3p9+Seuv8lnnoF5Z1{qhkflpx-mMbBs)?TjL_Fs>D7XRTg!`&^eC zTDdcE4;D6~6Ppmn=9QY$z%C7FDj+PKZ`{Ikg9vJ4iaH+?-XF)Dp91n35(S5leKF6C z^ugRN*sDP}s|_^ML@_F|e)P%9b6qj0FcIv@7?{*=2fE-ax5qqDg%+7K%^?bzqlS8i z3{2R9#-{pArd%yhn~nz>g%s?~X6~IgQ42jm%28mL!#vavBRKY^$&S+Ap%N*gy=WQ? z=cs3~Qk4o>-my5E!YA0KtjqvYmB5&Iv?Kz=s zXAXm=W&H9s^Q-9nWRU|XlidGLcjx}k^#4BoIYi2#c&SjRNOH;)IZcs}Qz=G{#gNmS z<~)=nQjTpoj*=)WlsRrnQZ~)`G)&5I)`X3jZJ*T--=E%p!T0&&^Y%PE9@q7_uE*oL zKd$Tfw4U60bytA#oRo1i!onH>h83eB2*1@c3%)0mHq`(M$OKfWVo~2-{hf`g!h?Fo zbZvmWes@iAH-XkoHgvm-aA;SC2$e&`wW14 zwj?S*mSP4NO~L2tOYQy=7mADT4g1zgfE`S}Aq=Ix75#5BWA$F~LF2fefEwY(V7=^m z6Xd4ep4o@0X|B~TNxlb$0s(c=GRYEGS6+!hf0nbu&&A*Ip33ion-@|108ho{=ow!z zAmLf3g{YBV6(98`dT!aJNmC;y31}^siL>R{Gk_#&S=O@BMGapDu7qby>GSfie(08L zvcegJ&hYJ`ENW4)uJ(~$G-q||K=NTje_`HLCqg~UcN)7b^@qFgJ@*sN?i~#I-Irc$ zCRG!W)#7QmN;5Q$9VZ;g+cX3qJK%<}F~s>m(+cdA6$(uS145jRcCAu7#(vOC44u#{nMiVM@DXg+*Lk17}FgTmXzs0sKDm48`+E@`rZz#Lgekp4lrE;`w{L;NK0R zo+(AOp_6>SfzWl7Xq3MvU9!5S6i|<=04QEziry%OWOA1E~nv_MeN3PYh&c~7rrCkr^VjT zih*qG zt=$po;uNQ52w>!E=M`)Tml5H)n74rT>?J_J;Psmw?waQh7{Y|bxIuXhP>!4k)%AlB z_e6#r|Jv)%1q~1N_o|-~57?Qj#O&*|omiOve94=pCE7WbuJ~cn8(?QlOT2k7S;cfG zpjv!Iwwyip+}U;T?Nf$VOLkQtl6D~3%pdo4X;e&>s^h?@Aefm0X6!_7MJl4fa=KE% z(W?36NvHj2=&ME+Pq2Xs5xE0B?O?#uXA^JavUlb+^F=QZUy9G1LHze#%pAmrf~v2C=Af*h;ZX+)7U8v-OrP;%@%tACoCvJ0;Uq z)35O&qtAwy?_G9ravvCiYa{lSzjoi$>if(H#3fxD_#PO3v?XI0GxB*n01S$d^dNsR zJUEEarWlI%%J&&s5`KEFj9CH1%h^#IaY{Ey>|zoMCYuZks>NxB+Wg1Q*USRcT-v%W zOZE5XGHwMVOH(%K7$n_sfXd6z?4u&^<7%V(DaNcH5bPixa7&b?16V*Du)c!wQfd8`m$-0iC8i9x9I zB#>)n=_xkcrlWaMz(3gA)f2KYY@dL*O}D>XvQ6&!wBc1UnJS za^b8dubr35sy7S~^)UK{3ePnk0XOSjM&9mLvxsYxdVnuZE0qDYq}cEOZSjfxbmB_K zN(c)#-=BEz&Pdx;gzbf+B2+bIt)<*os^@AV=`3}{wCS$V%Mz`kJcYcFRfdkF>}vvW z?CKZ3dhg%2&1IextrzD|OZ8a6HF$s#)Xvxb@`~llxg!b`qJdbab>)-+o9dqoSOw_? zi4QU~>gA5pnmXNw_?Dy#Q!TS^=?UL8q`fmd5PW&TFm!`Ah=fm)mdK-Ra|o8A@EvOC z!i*w7csWyRs&zUQ2~Oo!0*sDMYUT7Vn=+npwiERDE<9iao7smP@Q_Xx_Br59N#oS? zB$JSSA=9)j3g*SrLIJk`6h($&*jN|Eep6~s5a|>sXV*;BsB(2MB|D~wH+nbiFEiZL z!cQfrwkI&a!YoHXa!W7rOI zADkpb7i>^v)Q$KUxOwZTAB^LJ9uL(fyWC@9yX%Gk?Q$NVuE~99DEgQVBzpQbxf0I2 z5yN!z4N@Q47mF(g9h;1fpNP6}O#k|i#g067ICZq+cGJL2#;$%tP1k{>|1?IEs1{9O z6Peu)4Ocx27k-7~pX7ibO%Y-rF?AvG`9a81H#w_fuZ&|6xYX^FGRGy~X}Rl{q#>h| zc?zJ9!xo>q%H?oC7!%4l07YkLm!Z{fkuq3s4~aUO?5c5nva*eC9TuI(7|=2*Kx6B4zjPi zNKp86MxC_Do_bjhGmPPw!6Uo?-sJKpWt!Z!8+33oV{BP(=e=e(@x*Exhx3SO;k5f` z*IuZRL*g;T4OU7p*WEJqNHM?k^TOV()u-GZFgMT z#>mEdDGsxqI=T_5#Rd6+Hmnvp@@d9m+}-}TK7x)4N-mdMir~s4V6E9i`3Fg*hcz3O zHcYa7wS( z9v|ga0M}b++H!8%8V<6y3E~D9C82U@I3Q`-G9l|bIG>)O>W!0t7f6_|JW5N#;^Zw zPHZSx#SRk~cbz@+U~PHyBVo7yc!1iJNm0u9g7X`-z0vac=wSTTv{O(Tg8VW8O^!yj z?(K|Xt#Z@|7Ko8t*oRAzpMthpZ7}OC;~=-p;&U;%#EEQlIm{a2nLsw%UX1nla0+Fd zPozC3it*ZXX<}54$(d{P%c1|(@|xURs{_sARJDjp{C(d3COWNZw`cQM?Hu^vo+>%1 znU!@M=g?B2;LFEr>j6+({&Dv!0L}YNaGlMJ-HsKFo_g1^ETwA`G`ZaHam@*=A_=V` zJp3%bwgh_dQzAYe|7y3dFo&kr#;>nViT^zs>pV)-d?~XYa&MGbH_egDXCHKR!q;*o zT+rHw18A+Hyzi9bDV8pr{NUOm;1)(VFccH5w(VMW*r!0JPQDb+`KJJ3e}v!{eW@Ut z*>1tN+st-}N3J=s-h9o?w=T#QY_%oWI|4Wk2TKh^Iycra${QH1b&LoG?s<~TH0!kz z2n#R<;c?53vk@X`It!OCYIu{EM>d#WakZ_GNZDjI$5q{B^x-b)x#0Zk14*PUJ0xFo z%~Q>m*_|79ZwNQ@%OyHnX^*9joQ&MhlVB&x%FMFSM_x=xLqsewE_V$q{oXH#RycjYoQKjGgNwZc&+$Sn};jWHU~!6S}EFHEq5UP{UZ zz?D9RRNawlGTSlt0G*5CwI`uyee3a8hFjq~V@C(hWB1=(uQD6lv{izGT;zKfh1K{& z!}c)GweJ?3hU@aY2cT2oPIU^v?ZFcs033w?M&G7Mth33=grn^HfFLtSm~`k=s#+Vo zetq(h;Ox&_U{M6c-OUjc!532LBgX*puZ@BBcy)}h%Y5Q1`xaR;$zulU_<^-`nVCDS zd7-8II^*~&nk9E_?H(8v1lJ8X{vAg+lMZ^s@jp=W3o_>}DOQ#qEW5+!j6NivItE@! zbOe;{rM_*HlO&{_ARSgi3y7d$B0eCao?h#T#Q@>?&zJwWFDjGDK8BuIhoYwA7_!M@ zSMTG*(&DUlLwvAW1+361YFBD53=PSj&&Z#*G6%?qa^0uC)PYm|md@~ApG+oOKEkn^ z_a4%d1l$*_XRYlpzxqBhK^I6R-{7$%qW6&4LOgZy2Du4 zC`)&JCP)iPon`s+9zIw(7xYcQj7{-@0YjyQ<$oFZ?d9u!ocTq0Ryt|O)!M{r zuPXmUreG?wpL6%+l`H*&hh3$?{I6}R4DukO^R&fsxZGN&+#WA6U32Gka0HmJ?kB}} z-SfZmr%2@(O?&hEA=eRkl1*R;h_w*d5#D}gBeRe?T{Zsn zP`Yg@Uc_-H;))LRzvRcRFnaV|g(=+f2+1i1PH(zK^gYWix2iW zaOb={KCHUL2}tZwReUg59`9?|M6k*s5q3K4F@N2{?u?MnJ5M*Uw@8AX<|VK9k0Ha# zfC<0E$6S+=`YB9*DBo55571}y`b4s`&b;H>@dYbp&_q#5nonu9Wm(O7DWr<-W6+)$ zW!N;Gm!5Z|o*5uRf9(m;KTUCnz7+o=ZRcO<4^Fyc$H?hm@<72&z`vxO_QtskG`3%r z{^f59#lML$F}_;|gONOpbHi z>4qN!faK4HA$YX&g2fDaLm`HJ1>*U~RJ}}ZpGhz$;ZR0DuJbZr4~8urjc~Wb1X1ERekO{~kD|;Nu=Z90JSyH+dl7e~ zx7p2qmNNy|9U!qJT%uMG3wxMbDWGS|79<4#pv;~xW<7Q?u~ZW>DW-*@$>v^S7w@m? zPB4LFhaP={I@XBNKtyYz*sA1_s=b{j*Y({OvOWVJ!^m-^nm*W<+0oLbn8qXH)USTH zR7M1(n+lD0xB^~>{!FJ$JY#0Srn6yW399ST9Y}z{a4wT}enS3(64zJ*;%w8n4(->W=UYCt_^hs#c4bOVf8N|ZPA9-^k+#Lz73iMw#tq4f}r z;t0qQLgIvk7V11<8e4AZUXUJy(cH2e)rZ~jwO;vh!^h;ysOr|lWmjLYLz7P3Q@7_I zxwp*YpWEi&4x3-ShG_iJJ9`mPONCXWz;#bhl9VZb&O4|)=6d89L}_e%8HHU0C55bP zkFD*v3q`EQQoR{A(|5nd^K9_3+PU7Q8dEif$xUA-aR`=K>2Wl zWsbOi#n~2o>_@0?Tpf-7B9`e2q&112hGqMq3qSK?*NJ^hzvQh~M?)Yk(Lm=~D;LnW zaJ@Z!HwP88@LEPn@q_UWxt9c=FZg z7I-y5Ehj2c69PZE5iOalGT#^-9u6^^bapJDbaH2kZR%0(ybi<~x zH~oXCj(=IeTD`JDXb`+D2Grf$Jxjr9(yNq{8h`w9Zt@!&4pYdCL>9qCKDbYk{e=hPD2db29fSX9J(baURrhIxmHe7L6`9C?P^{vIjT1rEy>Ft z**4@~)6;uCDMnT6s(;sXK;*=Ek-a8rqgKM9oHL)Vm|rZ;5`AFi?M4W(AF!$ZOJAtQwicKlYLDhm`gh^_UjSzSI49qQ3`}8v9CC?^;tSS@cX8A z)(<-A$@y_&Gts>b82okd1|0z6;p)ab2>tPODpR}dT`p(*;kHE!1PzT@Z&(LLTX z2dCvIIIFUpa^J9J;!TycVLAM@l@{N2{2eaU;;HQvuP(GxK{goCgA zP)EI*nVxS)+(Z&YVD8!F*d2KDQRa+e^dJv46*t0Vqr^{gb(igw)LRujyM<@Nn6#2y z$TN5LPQa%hURRjs+?LWiQpbexzH`66U9~icXps_GsD`bx9MKN)g=}r<32Y%rDEOzJ zw2IidZ6dO`#yRxfRmvh_7hQ)(JeX_U)flxH2l&}P1h~;d(u_^ zZ*(f^6&Bjkk5hp=)jkHa=U3gJNb_y$HektS^oR{sd7EDrdfIAF$Ij{#i%}FWk?5~b zkC@7=8wLi36d-dTD{<&kh!pF^@W(BlIDAIyq!r-%1h%># zYp^&rPoSm@GvJUsu=ga9YG^oWRG@)7t@v*h7=x$)bhN&uM|-0--uDxtV)r z8#8sK8h-%v!C#iv|9*q&OwY zj8x!%H;ui?4k|obdJFj~gNhgHbfjLxSJ?2#$kzv^N%I$^laatlGe>8`)2grQH3imE ztH%URB~N`5$t>x|UZs$R^LJU#3Dhc_?Qip5y|HPPJ3Qxw{e_?u{n+Tdl7ryT6&X4+@Q!|C4(!bvlx$Fx zB*YOvj-3M3fdBK>Y8xc^Myp{XI%~-)Qk;iU)RsI(_P)TK-9ML+ESoXas1HNf2LUG! zst4vUu0`NO9x$G*Jh@yT_O;3Q0g`x6D~(b6sVPLhH^EcMh_VydF=3aV1|k*RsI1rf z>X|wQy;s+^^8!W2`)g<GF*mdI9~O7)s{m2)y&C$L*Wf0t_J!jGn|Ft@DR|$d!`i zaUL!4P6L3P)q`>7=k-ly%lHOZSQ(25-NON09KJJ{Edked8!*`iAJCs6|p z&vi+E4yej;cWO4U+*|!**zKY##p^A*e3OZy|T)_P3jkYB7oHf^mUsZ zLEd}C=uT%%`RSpI%daEzg6iX<7A6NDF*z2E3C1oF2R(306oc!~L({sidT-K%CNhgF z>PZHwl=p}WR#DfkOHheI6J>3OCZTkOil9k2kM_e^tf)D1^a&OCE>P=LqUfm=Suk%# zQUu^+$CpNObz}(Vf9X`;ONA+?)Vs)JKd}xSTBIW+@x^bv*GqUc!H+xxESBMYtsU>gGEu<}dAlcNPyZP-J zk}rl7gh4Rhh;Xr-;vxs_`!LxR@w0{ zK7)B0j^H{Nc?KUHD1)i6lW1l}RZOK-7*AQt+}%5X@x)Y!$~v4%`+a#BJ0a{{+3pSg zOtdv#qZ={8G0q+7bZrPEPKos43<@PeML9NVeQeF8vDG)0X_vqyRSob=ZhBSq+D^%S zUe?-e5biX69xOC_uOFND^X7`>jnU=LyYMof0@+C3>^6pt{+Bn+cR}}^%RVhs?J)AP zV~1t+kpJa!o%kRMRo$Awx34fU$*Shki=6n&u7OXH{6SuzboAS z+OL6EM<`{SmsRk*n?WXKZ=I+)xl!;6(Q5El}q%0gpf8F*9f&?E#WtDUmS0u(Zy;jnnr!JQ|)N5Rg^d zx%#+>@!JK7(I~PA8pS97`2AvAcA}6zZ0@_KIW}+o$eX>L=Qs2^q&Z+AFO9ZZOV1jd z-zqC-9C(A8c;aW{Ut4s_<1^E_iOW`|x0Gul^h@gr9*FCentUpY@hU#=Jk(^)jS>>* zpt{9J>VYqmDk9ss_eD45jB8Mw)xJnyKqfrug*|=fyWU_=+~A|(gS95pL73Y3lgcDX z*ASD*uCkh~n+xCeCFKU6_4f4dI7Zz5J&Hi&C{(pLQEs(9^A2a$w!dCzI-!592+g+|R^}7Fm$Ob^i diff --git a/cmd/clef/docs/clef_architecture_pt3.png b/cmd/clef/docs/clef_architecture_pt3.png deleted file mode 100644 index b9d695447308c0fe7c0dc3018618f7350f2a806c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 101351 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%0Amw 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≺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 diff --git a/cmd/clef/docs/qubes/clef_qubes_http.png b/cmd/clef/docs/qubes/clef_qubes_http.png deleted file mode 100644 index e95ad8da4af6fdd4f628ec4ea741c0b98147f00b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12237 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

>W^cs-?#mXItenZY6gYiNlBTK1l?7OkpEdc1 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 GkN+2l3G_?= diff --git a/cmd/clef/docs/qubes/qrexec-example.png b/cmd/clef/docs/qubes/qrexec-example.png deleted file mode 100644 index fc5d57725dd30f32415e543244c9c40110a31c8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16166 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 diff --git a/cmd/clef/docs/qubes/qubes-client.py b/cmd/clef/docs/qubes/qubes-client.py deleted file mode 100644 index 93a74b899b..0000000000 --- a/cmd/clef/docs/qubes/qubes-client.py +++ /dev/null @@ -1,23 +0,0 @@ -""" -This implements a dispatcher which listens to localhost:8550, and proxies -requests via qrexec to the service qubes.EthSign on a target domain -""" - -import http.server -import socketserver,subprocess - -PORT=8550 -TARGET_DOMAIN= 'debian-work' - -class Dispatcher(http.server.BaseHTTPRequestHandler): - def do_POST(self): - post_data = self.rfile.read(int(self.headers['Content-Length'])) - p = subprocess.Popen(['/usr/bin/qrexec-client-vm',TARGET_DOMAIN,'qubes.Clefsign'],stdin=subprocess.PIPE, stdout=subprocess.PIPE) - output = p.communicate(post_data)[0] - self.wfile.write(output) - - -with socketserver.TCPServer(("",PORT), Dispatcher) as httpd: - print("Serving at port", PORT) - httpd.serve_forever() - diff --git a/cmd/clef/docs/qubes/qubes.Clefsign b/cmd/clef/docs/qubes/qubes.Clefsign deleted file mode 100644 index 9b5af7b4fe..0000000000 --- a/cmd/clef/docs/qubes/qubes.Clefsign +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -SIGNER_BIN="/home/user/tools/clef/clef" -SIGNER_CMD="/home/user/tools/gtksigner/gtkui.py -s $SIGNER_BIN" - -# Start clef if not already started -if [ ! -S /home/user/.clef/clef.ipc ]; then - $SIGNER_CMD & - sleep 1 -fi - -# Should be started by now -if [ -S /home/user/.clef/clef.ipc ]; then - # Post incoming request to HTTP channel - curl -H "Content-Type: application/json" -X POST -d @- http://localhost:8550 2>/dev/null -fi diff --git a/cmd/clef/docs/qubes/qubes_newaccount-1.png b/cmd/clef/docs/qubes/qubes_newaccount-1.png deleted file mode 100644 index 3bfc8b5b7e91330f1f773d9fef259906eab06c31..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22348 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 IQcs@$AA2scU;qFB diff --git a/cmd/clef/docs/setup.md b/cmd/clef/docs/setup.md deleted file mode 100644 index 6cc7a4120d..0000000000 --- a/cmd/clef/docs/setup.md +++ /dev/null @@ -1,198 +0,0 @@ -# Setting up Clef - -This document describes how Clef can be used in a more secure manner than executing it from your everyday laptop, -in order to ensure that the keys remain safe in the event that your computer should get compromised. - -## Qubes OS - - -### Background - -The Qubes operating system is based around virtual machines (qubes), where a set of virtual machines are configured, typically for -different purposes such as: - -- personal - - Your personal email, browsing etc -- work - - Work email etc -- vault - - a VM without network access, where gpg-keys and/or keepass credentials are stored. - -A couple of dedicated virtual machines handle externalities: - -- sys-net provides networking to all other (network-enabled) machines -- sys-firewall handles firewall rules -- sys-usb handles USB devices, and can map usb-devices to certain qubes. - -The goal of this document is to describe how we can set up clef to provide secure transaction -signing from a `vault` vm, to another networked qube which runs Dapps. - -### Setup - -There are two ways that this can be achieved: integrated via Qubes or integrated via networking. - - -#### 1. Qubes Integrated - -Qubes provides a facility for inter-qubes communication via `qrexec`. A qube can request to make a cross-qube RPC request -to another qube. The OS then asks the user if the call is permitted. - -![Example](qubes/qrexec-example.png) - -A policy-file can be created to allow such interaction. On the `target` domain, a service is invoked which can read the -`stdin` from the `client` qube. - -This is how [Split GPG](https://www.qubes-os.org/doc/split-gpg/) is implemented. We can set up Clef the same way: - -##### Server - -![Clef via qrexec](qubes/clef_qubes_qrexec.png) - -On the `target` qubes, we need to define the RPC service. - -[qubes.Clefsign](qubes/qubes.Clefsign): - -```bash -#!/bin/bash - -SIGNER_BIN="/home/user/tools/clef/clef" -SIGNER_CMD="/home/user/tools/gtksigner/gtkui.py -s $SIGNER_BIN" - -# Start clef if not already started -if [ ! -S /home/user/.clef/clef.ipc ]; then - $SIGNER_CMD & - sleep 1 -fi - -# Should be started by now -if [ -S /home/user/.clef/clef.ipc ]; then - # Post incoming request to HTTP channel - curl -H "Content-Type: application/json" -X POST -d @- http://localhost:8550 2>/dev/null -fi - -``` -This RPC service is not complete (see notes about HTTP headers below), but works as a proof-of-concept. -It will forward the data received on `stdin` (forwarded by the OS) to Clef's HTTP channel. - -It would have been possible to send data directly to the `/home/user/.clef/.clef.ipc` -socket via e.g `nc -U /home/user/.clef/clef.ipc`, but the reason for sending the request -data over `HTTP` instead of `IPC` is that we want the ability to forward `HTTP` headers. - -To enable the service: - -``` bash -sudo cp qubes.Clefsign /etc/qubes-rpc/ -sudo chmod +x /etc/qubes-rpc/ qubes.Clefsign -``` - -This setup uses [gtksigner](https://github.com/holiman/gtksigner), which is a very minimal GTK-based UI that works well -with minimal requirements. - -##### Client - - -On the `client` qube, we need to create a listener which will receive the request from the Dapp, and proxy it. - - -[qubes-client.py](qubes/qubes-client.py): - -```python - -""" -This implements a dispatcher which listens to localhost:8550, and proxies -requests via qrexec to the service qubes.EthSign on a target domain -""" - -import http.server -import socketserver,subprocess - -PORT=8550 -TARGET_DOMAIN= 'debian-work' - -class Dispatcher(http.server.BaseHTTPRequestHandler): - def do_POST(self): - post_data = self.rfile.read(int(self.headers['Content-Length'])) - p = subprocess.Popen(['/usr/bin/qrexec-client-vm',TARGET_DOMAIN,'qubes.Clefsign'],stdin=subprocess.PIPE, stdout=subprocess.PIPE) - output = p.communicate(post_data)[0] - self.wfile.write(output) - - -with socketserver.TCPServer(("",PORT), Dispatcher) as httpd: - print("Serving at port", PORT) - httpd.serve_forever() - - -``` - -#### Testing - -To test the flow, if we have set up `debian-work` as the `target`, we can do - -```bash -$ cat newaccnt.json -{ "id": 0, "jsonrpc": "2.0","method": "account_new","params": []} - -$ cat newaccnt.json| qrexec-client-vm debian-work qubes.Clefsign -``` - -A dialog should pop up first to allow the IPC call: - -![one](qubes/qubes_newaccount-1.png) - -Followed by a GTK-dialog to approve the operation: - -![two](qubes/qubes_newaccount-2.png) - -To test the full flow, we use the client wrapper. Start it on the `client` qube: -``` -[user@work qubes]$ python3 qubes-client.py -``` - -Make the request over http (`client` qube): -``` -[user@work clef]$ cat newaccnt.json | curl -X POST -d @- http://localhost:8550 -``` -And it should show the same popups again. - -##### Pros and cons - -The benefits of this setup are: - -- This is the qubes-os intended model for inter-qube communication, -- and thus benefits from qubes-os dialogs and policies for user approval - -However, it comes with a couple of drawbacks: - -- The `qubes-gpg-client` must forward the http request via RPC to the `target` qube. When doing so, the proxy - will either drop important headers, or replace them. - - The `Host` header is most likely `localhost` - - The `Origin` header must be forwarded - - Information about the remote ip must be added as a `X-Forwarded-For`. However, Clef cannot always trust an `XFF` header, - since malicious clients may lie about `XFF` in order to fool the http server into believing it comes from another address. -- Even with a policy in place to allow RPC calls between `caller` and `target`, there will be several popups: - - One qubes-specific where the user specifies the `target` vm - - One clef-specific to approve the transaction - - -#### 2. Network integrated - -The second way to set up Clef on a qubes system is to allow networking, and have Clef listen to a port which is accessible -from other qubes. - -![Clef via http](qubes/clef_qubes_http.png) - - - - -## USBArmory - -The [USB armory](https://inversepath.com/usbarmory) is an open source hardware design with an 800 MHz ARM processor. It is a pocket-size -computer. When inserted into a laptop, it identifies itself as a USB network interface, basically adding another network -to your computer. Over this new network interface, you can SSH into the device. - -Running Clef off a USB armory means that you can use the armory as a very versatile offline computer, which only -ever connects to a local network between your computer and the device itself. - -Needless to say, while this model should be fairly secure against remote attacks, an attacker with physical access -to the USB Armory would trivially be able to extract the contents of the device filesystem. - diff --git a/cmd/clef/extapi_changelog.md b/cmd/clef/extapi_changelog.md deleted file mode 100644 index be84e88c52..0000000000 --- a/cmd/clef/extapi_changelog.md +++ /dev/null @@ -1,104 +0,0 @@ -## Changelog for external API - -The API uses [semantic versioning](https://semver.org/). - -TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the: - -* MAJOR version when you make incompatible API changes, -* MINOR version when you add functionality in a backwards-compatible manner, and -* PATCH version when you make backwards-compatible bug fixes. - -Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. - -### 6.1.0 - -The API-method `account_signGnosisSafeTx` was added. This method takes two parameters, -`[address, safeTx]`. The latter, `safeTx`, can be copy-pasted from the gnosis relay. For example: - -``` -{ - "jsonrpc": "2.0", - "method": "account_signGnosisSafeTx", - "params": ["0xfd1c4226bfD1c436672092F4eCbfC270145b7256", - { - "safe": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3", - "to": "0xB372a646f7F05Cc1785018dBDA7EBc734a2A20E2", - "value": "20000000000000000", - "data": null, - "operation": 0, - "gasToken": "0x0000000000000000000000000000000000000000", - "safeTxGas": 27845, - "baseGas": 0, - "gasPrice": "0", - "refundReceiver": "0x0000000000000000000000000000000000000000", - "nonce": 2, - "executionDate": null, - "submissionDate": "2020-09-15T21:54:49.617634Z", - "modified": "2020-09-15T21:54:49.617634Z", - "blockNumber": null, - "transactionHash": null, - "safeTxHash": "0x2edfbd5bc113ff18c0631595db32eb17182872d88d9bf8ee4d8c2dd5db6d95e2", - "executor": null, - "isExecuted": false, - "isSuccessful": null, - "ethGasPrice": null, - "gasUsed": null, - "fee": null, - "origin": null, - "dataDecoded": null, - "confirmationsRequired": null, - "confirmations": [ - { - "owner": "0xAd2e180019FCa9e55CADe76E4487F126Fd08DA34", - "submissionDate": "2020-09-15T21:54:49.663299Z", - "transactionHash": null, - "confirmationType": "CONFIRMATION", - "signature": "0x95a7250bb645f831c86defc847350e7faff815b2fb586282568e96cc859e39315876db20a2eed5f7a0412906ec5ab57652a6f645ad4833f345bda059b9da2b821c", - "signatureType": "EOA" - } - ], - "signatures": null - } - ], - "id": 67 -} -``` - -Not all fields are required, though. This method is really just a UX helper, which massages the -input to conform to the `EIP-712` [specification](https://docs.safe.global/core-api/transaction-service-reference/gnosis) -for the Gnosis Safe, and making the output be directly importable to by a relay service. - - -### 6.0.0 - -* `New` was changed to deliver only an address, not the full `Account` data -* `Export` was moved from External API to the UI Server API - -#### 5.0.0 - -* The external `account_EcRecover`-method was reimplemented. -* The external method `account_sign(address, data)` was replaced with `account_signData(contentType, address, data)`. -The addition of `contentType` makes it possible to use the method for different types of objects, such as: - * signing data with an intended validator (not yet implemented) - * signing clique headers, - * signing plain personal messages, -* The external method `account_signTypedData` implements [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and makes it possible to sign typed data. - -#### 4.0.0 - -* The external `account_Ecrecover`-method was removed. -* The external `account_Import`-method was removed. - -#### 3.0.0 - -* The external `account_List`-method was changed to not expose `url`, which contained info about the local filesystem. It now returns only a list of addresses. - -#### 2.0.0 - -* Commit `73abaf04b1372fa4c43201fb1b8019fe6b0a6f8d`, move `from` into `transaction` object in `signTransaction`. This -makes the `accounts_signTransaction` identical to the old `eth_signTransaction`. - - -#### 1.0.0 - -Initial release. diff --git a/cmd/clef/intapi_changelog.md b/cmd/clef/intapi_changelog.md deleted file mode 100644 index 85d04f6d0e..0000000000 --- a/cmd/clef/intapi_changelog.md +++ /dev/null @@ -1,191 +0,0 @@ -## Changelog for internal API (ui-api) - -The API uses [semantic versioning](https://semver.org/). - -TL;DR: Given a version number MAJOR.MINOR.PATCH, increment the: - -* MAJOR version when you make incompatible API changes, -* MINOR version when you add functionality in a backwards-compatible manner, and -* PATCH version when you make backwards-compatible bug fixes. - -Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format. - -### 7.0.1 - -Added `clef_New` to the internal API callable from a UI. - -> `New` creates a new password protected Account. The private key is protected with -> the given password. Users are responsible to backup the private key that is stored -> in the keystore location that was specified when this API was created. -> This method is the same as New on the external API, the difference being that -> this implementation does not ask for confirmation, since it's initiated by -> the user - -### 7.0.0 - -- The `message` field was renamed to `messages` in all data signing request methods to better reflect that it's a list, not a value. -- The `storage.Put` and `storage.Get` methods in the rule execution engine were lower-cased to `storage.put` and `storage.get` to be consistent with JavaScript call conventions. - -### 6.0.0 - -Removed `password` from responses to operations which require them. This is for two reasons, - -- Consistency between how rulesets operate and how manual processing works. A rule can `Approve` but require the actual password to be stored in the clef storage. -With this change, the same stored password can be used even if rulesets are not enabled, but storage is. -- It also removes the usability-shortcut that a UI might otherwise want to implement; remembering passwords. Since we now will not require the -password on every `Approve`, there's no need for the UI to cache it locally. - - In a future update, we'll likely add `clef_storePassword` to the internal API, so the user can store it via his UI (currently only CLI works). - -Affected datatypes: -- `SignTxResponse` -- `SignDataResponse` -- `NewAccountResponse` - -If `clef` requires a password, the `OnInputRequired` will be used to collect it. - - -### 5.0.0 - -Changed the namespace format to adhere to the legacy ethereum format: `name_methodName`. Changes: - -* `ApproveTx` -> `ui_approveTx` -* `ApproveSignData` -> `ui_approveSignData` -* `ApproveExport` -> `removed` -* `ApproveImport` -> `removed` -* `ApproveListing` -> `ui_approveListing` -* `ApproveNewAccount` -> `ui_approveNewAccount` -* `ShowError` -> `ui_showError` -* `ShowInfo` -> `ui_showInfo` -* `OnApprovedTx` -> `ui_onApprovedTx` -* `OnSignerStartup` -> `ui_onSignerStartup` -* `OnInputRequired` -> `ui_onInputRequired` - - -### 4.0.0 - -* Bidirectional communication implemented, so the UI can query `clef` via the stdin/stdout RPC channel. Methods implemented are: - - `clef_listWallets` - - `clef_listAccounts` - - `clef_listWallets` - - `clef_deriveAccount` - - `clef_importRawKey` - - `clef_openWallet` - - `clef_chainId` - - `clef_setChainId` - - `clef_export` - - `clef_import` - -* The type `Account` was modified (the json-field `type` was removed), to consist of - -```go -type Account struct { - Address common.Address `json:"address"` // Ethereum account address derived from the key - URL URL `json:"url"` // Optional resource locator within a backend -} -``` - - -### 3.2.0 - -* Make `ShowError`, `OnApprovedTx`, `OnSignerStartup` be json-rpc [notifications](https://www.jsonrpc.org/specification#notification): - -> A Notification is a Request object without an "id" member. A Request object that is a Notification signifies the Client's lack of interest in the corresponding Response object, and as such no Response object needs to be returned to the client. The Server MUST NOT reply to a Notification, including those that are within a batch request. -> -> Notifications are not confirmable by definition, since they do not have a Response object to be returned. As such, the Client would not be aware of any errors (like e.g. "Invalid params","Internal error" -### 3.1.0 - -* Add `ContentType` `string` to `SignDataRequest` to accommodate the latest [EIP-191](https://eips.ethereum.org/EIPS/eip-191) and [EIP-712](https://eips.ethereum.org/EIPS/eip-712) implementations. - -### 3.0.0 - -* Make use of `OnInputRequired(info UserInputRequest)` for obtaining master password during startup - -### 2.1.0 - -* Add `OnInputRequired(info UserInputRequest)` to internal API. This method is used when Clef needs user input, e.g. passwords. - -The following structures are used: - -```go -UserInputRequest struct { - Prompt string `json:"prompt"` - Title string `json:"title"` - IsPassword bool `json:"isPassword"` -} -UserInputResponse struct { - Text string `json:"text"` -} -``` - -### 2.0.0 - -* Modify how `call_info` on a transaction is conveyed. New format: - -``` -{ - "jsonrpc": "2.0", - "id": 2, - "method": "ApproveTx", - "params": [ - { - "transaction": { - "from": "0x82A2A876D39022B3019932D30Cd9c97ad5616813", - "to": "0x07a565b7ed7d7a678680a4c162885bedbb695fe0", - "gas": "0x333", - "gasPrice": "0x123", - "value": "0x10", - "nonce": "0x0", - "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012", - "input": null - }, - "call_info": [ - { - "type": "WARNING", - "message": "Invalid checksum on to-address" - }, - { - "type": "WARNING", - "message": "Tx contains data, but provided ABI signature could not be matched: Did not match: test (0 matches)" - } - ], - "meta": { - "remote": "127.0.0.1:54286", - "local": "localhost:8550", - "scheme": "HTTP/1.1" - } - } - ] -} -``` - -#### 1.2.0 - -* Add `OnStartup` method, to provide the UI with information about what API version -the signer uses (both internal and external) as well as build-info and external api. - -Example call: -```json -{ - "jsonrpc": "2.0", - "id": 1, - "method": "OnSignerStartup", - "params": [ - { - "info": { - "extapi_http": "http://localhost:8550", - "extapi_ipc": null, - "extapi_version": "2.0.0", - "intapi_version": "1.2.0" - } - } - ] -} -``` - -#### 1.1.0 - -* Add `OnApproved` method - -#### 1.0.0 - -Initial release. diff --git a/cmd/clef/main.go b/cmd/clef/main.go deleted file mode 100644 index 2b54bb14cb..0000000000 --- a/cmd/clef/main.go +++ /dev/null @@ -1,1222 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package main - -import ( - "bufio" - "context" - "crypto/rand" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "math/big" - "net" - "os" - "os/signal" - "path/filepath" - "runtime" - "strings" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/cmd/utils" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/internal/flags" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/signer/core" - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/ethereum/go-ethereum/signer/fourbyte" - "github.com/ethereum/go-ethereum/signer/rules" - "github.com/ethereum/go-ethereum/signer/storage" - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" - "github.com/urfave/cli/v2" -) - -const legalWarning = ` -WARNING! - -Clef is an account management tool. It may, like any software, contain bugs. - -Please take care to -- backup your keystore files, -- verify that the keystore(s) can be opened with your password. - -Clef is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. -` - -var ( - logLevelFlag = &cli.IntFlag{ - Name: "loglevel", - Value: 3, - Usage: "log level to emit to the screen", - } - advancedMode = &cli.BoolFlag{ - Name: "advanced", - Usage: "If enabled, issues warnings instead of rejections for suspicious requests. Default off", - } - acceptFlag = &cli.BoolFlag{ - Name: "suppress-bootwarn", - Usage: "If set, does not show the warning during boot", - } - keystoreFlag = &cli.StringFlag{ - Name: "keystore", - Value: filepath.Join(node.DefaultDataDir(), "keystore"), - Usage: "Directory for the keystore", - } - configdirFlag = &cli.StringFlag{ - Name: "configdir", - Value: DefaultConfigDir(), - Usage: "Directory for Clef configuration", - } - chainIdFlag = &cli.Int64Flag{ - Name: "chainid", - Value: params.MainnetChainConfig.ChainID.Int64(), - Usage: "Chain id to use for signing (1=mainnet, 17000=Holesky)", - } - rpcPortFlag = &cli.IntFlag{ - Name: "http.port", - Usage: "HTTP-RPC server listening port", - Value: node.DefaultHTTPPort + 5, - Category: flags.APICategory, - } - signerSecretFlag = &cli.StringFlag{ - Name: "signersecret", - Usage: "A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash", - } - customDBFlag = &cli.StringFlag{ - Name: "4bytedb-custom", - Usage: "File used for writing new 4byte-identifiers submitted via API", - Value: "./4byte-custom.json", - } - auditLogFlag = &cli.StringFlag{ - Name: "auditlog", - Usage: "File used to emit audit logs. Set to \"\" to disable", - Value: "audit.log", - } - ruleFlag = &cli.StringFlag{ - Name: "rules", - Usage: "Path to the rule file to auto-authorize requests with", - } - stdiouiFlag = &cli.BoolFlag{ - Name: "stdio-ui", - Usage: "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.", - } - testFlag = &cli.BoolFlag{ - Name: "stdio-ui-test", - Usage: "Mechanism to test interface between Clef and UI. Requires 'stdio-ui'.", - } - initCommand = &cli.Command{ - Action: initializeSecrets, - Name: "init", - Usage: "Initialize the signer, generate secret storage", - ArgsUsage: "", - Flags: []cli.Flag{ - logLevelFlag, - configdirFlag, - }, - Description: ` -The init command generates a master seed which Clef can use to store credentials and data needed for -the rule-engine to work.`, - } - attestCommand = &cli.Command{ - Action: attestFile, - Name: "attest", - Usage: "Attest that a js-file is to be used", - ArgsUsage: "", - Flags: []cli.Flag{ - logLevelFlag, - configdirFlag, - signerSecretFlag, - }, - Description: ` -The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of -incoming requests. - -Whenever you make an edit to the rule file, you need to use attestation to tell -Clef that the file is 'safe' to execute.`, - } - setCredentialCommand = &cli.Command{ - Action: setCredential, - Name: "setpw", - Usage: "Store a credential for a keystore file", - ArgsUsage: "

", - Flags: []cli.Flag{ - logLevelFlag, - configdirFlag, - signerSecretFlag, - }, - Description: ` -The setpw command stores a password for a given address (keyfile). -`} - delCredentialCommand = &cli.Command{ - Action: removeCredential, - Name: "delpw", - Usage: "Remove a credential for a keystore file", - ArgsUsage: "
", - Flags: []cli.Flag{ - logLevelFlag, - configdirFlag, - signerSecretFlag, - }, - Description: ` -The delpw command removes a password for a given address (keyfile). -`} - newAccountCommand = &cli.Command{ - Action: newAccount, - Name: "newaccount", - Usage: "Create a new account", - ArgsUsage: "", - Flags: []cli.Flag{ - logLevelFlag, - keystoreFlag, - utils.LightKDFFlag, - acceptFlag, - }, - Description: ` -The newaccount command creates a new keystore-backed account. It is a convenience-method -which can be used in lieu of an external UI. -`} - gendocCommand = &cli.Command{ - Action: GenDoc, - Name: "gendoc", - Usage: "Generate documentation about json-rpc format", - Description: ` -The gendoc generates example structures of the json-rpc communication types. -`} - listAccountsCommand = &cli.Command{ - Action: listAccounts, - Name: "list-accounts", - Usage: "List accounts in the keystore", - Flags: []cli.Flag{ - logLevelFlag, - keystoreFlag, - utils.LightKDFFlag, - acceptFlag, - }, - Description: ` - Lists the accounts in the keystore. - `} - listWalletsCommand = &cli.Command{ - Action: listWallets, - Name: "list-wallets", - Usage: "List wallets known to Clef", - Flags: []cli.Flag{ - logLevelFlag, - keystoreFlag, - utils.LightKDFFlag, - acceptFlag, - }, - Description: ` - Lists the wallets known to Clef. - `} - importRawCommand = &cli.Command{ - Action: accountImport, - Name: "importraw", - Usage: "Import a hex-encoded private key.", - ArgsUsage: "", - Flags: []cli.Flag{ - logLevelFlag, - keystoreFlag, - utils.LightKDFFlag, - acceptFlag, - }, - Description: ` -Imports an unencrypted private key from and creates a new account. -Prints the address. -The keyfile is assumed to contain an unencrypted private key in hexadecimal format. -The account is saved in encrypted format, you are prompted for a password. -`} -) - -var app = flags.NewApp("Manage Ethereum account operations") - -func init() { - app.Name = "Clef" - app.Flags = []cli.Flag{ - logLevelFlag, - keystoreFlag, - configdirFlag, - chainIdFlag, - utils.LightKDFFlag, - utils.SmartCardDaemonPathFlag, - utils.HTTPListenAddrFlag, - utils.HTTPVirtualHostsFlag, - utils.IPCDisabledFlag, - utils.IPCPathFlag, - utils.HTTPEnabledFlag, - rpcPortFlag, - signerSecretFlag, - customDBFlag, - auditLogFlag, - ruleFlag, - stdiouiFlag, - testFlag, - advancedMode, - acceptFlag, - } - app.Action = signer - app.Commands = []*cli.Command{initCommand, - attestCommand, - setCredentialCommand, - delCredentialCommand, - newAccountCommand, - importRawCommand, - gendocCommand, - listAccountsCommand, - listWalletsCommand, - } -} - -func main() { - if err := app.Run(os.Args); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -} - -func initializeSecrets(c *cli.Context) error { - // Get past the legal message - if err := initialize(c); err != nil { - return err - } - // Ensure the master key does not yet exist, we're not willing to overwrite - configDir := c.String(configdirFlag.Name) - if err := os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) { - return err - } - location := filepath.Join(configDir, "masterseed.json") - if _, err := os.Stat(location); err == nil { - return fmt.Errorf("master key %v already exists, will not overwrite", location) - } - // Key file does not exist yet, generate a new one and encrypt it - masterSeed := make([]byte, 256) - num, err := io.ReadFull(rand.Reader, masterSeed) - if err != nil { - return err - } - if num != len(masterSeed) { - return errors.New("failed to read enough random") - } - n, p := keystore.StandardScryptN, keystore.StandardScryptP - if c.Bool(utils.LightKDFFlag.Name) { - n, p = keystore.LightScryptN, keystore.LightScryptP - } - text := "The master seed of clef will be locked with a password.\nPlease specify a password. Do not forget this password!" - var password string - for { - password = utils.GetPassPhrase(text, true) - if err := core.ValidatePasswordFormat(password); err != nil { - fmt.Printf("invalid password: %v\n", err) - } else { - fmt.Println() - break - } - } - cipherSeed, err := encryptSeed(masterSeed, []byte(password), n, p) - if err != nil { - return fmt.Errorf("failed to encrypt master seed: %v", err) - } - // Double check the master key path to ensure nothing wrote there in between - if err = os.Mkdir(configDir, 0700); err != nil && !os.IsExist(err) { - return err - } - if _, err := os.Stat(location); err == nil { - return fmt.Errorf("master key %v already exists, will not overwrite", location) - } - // Write the file and print the usual warning message - if err = os.WriteFile(location, cipherSeed, 0400); err != nil { - return err - } - fmt.Printf("A master seed has been generated into %s\n", location) - fmt.Printf(` -This is required to be able to store credentials, such as: -* Passwords for keystores (used by rule engine) -* Storage for JavaScript auto-signing rules -* Hash of JavaScript rule-file - -You should treat 'masterseed.json' with utmost secrecy and make a backup of it! -* The password is necessary but not enough, you need to back up the master seed too! -* The master seed does not contain your accounts, those need to be backed up separately! - -`) - return nil -} - -func attestFile(ctx *cli.Context) error { - if ctx.NArg() < 1 { - utils.Fatalf("This command requires an argument.") - } - if err := initialize(ctx); err != nil { - return err - } - - stretchedKey, err := readMasterKey(ctx, nil) - if err != nil { - utils.Fatalf(err.Error()) - } - configDir := ctx.String(configdirFlag.Name) - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) - confKey := crypto.Keccak256([]byte("config"), stretchedKey) - - // Initialize the encrypted storages - configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confKey) - val := ctx.Args().First() - configStorage.Put("ruleset_sha256", val) - log.Info("Ruleset attestation updated", "sha256", val) - return nil -} - -func initInternalApi(c *cli.Context) (*core.UIServerAPI, core.UIClientAPI, error) { - if err := initialize(c); err != nil { - return nil, nil, err - } - var ( - ui = core.NewCommandlineUI() - pwStorage storage.Storage = &storage.NoStorage{} - ksLoc = c.String(keystoreFlag.Name) - lightKdf = c.Bool(utils.LightKDFFlag.Name) - ) - am := core.StartClefAccountManager(ksLoc, lightKdf, "") - api := core.NewSignerAPI(am, 0, ui, nil, false, pwStorage) - internalApi := core.NewUIServerAPI(api) - return internalApi, ui, nil -} - -func setCredential(ctx *cli.Context) error { - if ctx.NArg() < 1 { - utils.Fatalf("This command requires an address to be passed as an argument") - } - if err := initialize(ctx); err != nil { - return err - } - addr := ctx.Args().First() - if !common.IsHexAddress(addr) { - utils.Fatalf("Invalid address specified: %s", addr) - } - address := common.HexToAddress(addr) - password := utils.GetPassPhrase("Please enter a password to store for this address:", true) - fmt.Println() - - stretchedKey, err := readMasterKey(ctx, nil) - if err != nil { - utils.Fatalf(err.Error()) - } - configDir := ctx.String(configdirFlag.Name) - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) - pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) - - pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) - pwStorage.Put(address.Hex(), password) - - log.Info("Credential store updated", "set", address) - return nil -} - -func removeCredential(ctx *cli.Context) error { - if ctx.NArg() < 1 { - utils.Fatalf("This command requires an address to be passed as an argument") - } - if err := initialize(ctx); err != nil { - return err - } - addr := ctx.Args().First() - if !common.IsHexAddress(addr) { - utils.Fatalf("Invalid address specified: %s", addr) - } - address := common.HexToAddress(addr) - - stretchedKey, err := readMasterKey(ctx, nil) - if err != nil { - utils.Fatalf(err.Error()) - } - configDir := ctx.String(configdirFlag.Name) - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) - pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) - - pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) - pwStorage.Del(address.Hex()) - - log.Info("Credential store updated", "unset", address) - return nil -} - -func initialize(c *cli.Context) error { - // Set up the logger to print everything - logOutput := os.Stdout - if c.Bool(stdiouiFlag.Name) { - logOutput = os.Stderr - // If using the stdioui, we can't do the 'confirm'-flow - if !c.Bool(acceptFlag.Name) { - fmt.Fprint(logOutput, legalWarning) - } - } else if !c.Bool(acceptFlag.Name) { - if !confirm(legalWarning) { - return errors.New("aborted by user") - } - fmt.Println() - } - usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - output := io.Writer(logOutput) - if usecolor { - output = colorable.NewColorable(logOutput) - } - verbosity := log.FromLegacyLevel(c.Int(logLevelFlag.Name)) - log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(output, verbosity, usecolor))) - - return nil -} - -func newAccount(c *cli.Context) error { - internalApi, _, err := initInternalApi(c) - if err != nil { - return err - } - addr, err := internalApi.New(context.Background()) - if err == nil { - fmt.Printf("Generated account %v\n", addr.String()) - } - return err -} - -func listAccounts(c *cli.Context) error { - internalApi, _, err := initInternalApi(c) - if err != nil { - return err - } - accs, err := internalApi.ListAccounts(context.Background()) - if err != nil { - return err - } - if len(accs) == 0 { - fmt.Println("\nThe keystore is empty.") - } - fmt.Println() - for _, account := range accs { - fmt.Printf("%v (%v)\n", account.Address, account.URL) - } - return err -} - -func listWallets(c *cli.Context) error { - internalApi, _, err := initInternalApi(c) - if err != nil { - return err - } - wallets := internalApi.ListWallets() - if len(wallets) == 0 { - fmt.Println("\nThere are no wallets.") - } - fmt.Println() - for i, wallet := range wallets { - fmt.Printf("- Wallet %d at %v (%v %v)\n", i, wallet.URL, wallet.Status, wallet.Failure) - for j, acc := range wallet.Accounts { - fmt.Printf(" -Account %d: %v (%v)\n", j, acc.Address, acc.URL) - } - fmt.Println() - } - return nil -} - -// accountImport imports a raw hexadecimal private key via CLI. -func accountImport(c *cli.Context) error { - if c.Args().Len() != 1 { - return errors.New(" must be given as first argument") - } - internalApi, ui, err := initInternalApi(c) - if err != nil { - return err - } - pKey, err := crypto.LoadECDSA(c.Args().First()) - if err != nil { - return err - } - readPw := func(prompt string) (string, error) { - resp, err := ui.OnInputRequired(core.UserInputRequest{ - Title: "Password", - Prompt: prompt, - IsPassword: true, - }) - if err != nil { - return "", err - } - return resp.Text, nil - } - first, err := readPw("Please enter a password for the imported account") - if err != nil { - return err - } - second, err := readPw("Please repeat the password you just entered") - if err != nil { - return err - } - if first != second { - //lint:ignore ST1005 This is a message for the user - return errors.New("Passwords do not match") - } - acc, err := internalApi.ImportRawKey(hex.EncodeToString(crypto.FromECDSA(pKey)), first) - if err != nil { - return err - } - ui.ShowInfo(fmt.Sprintf(`Key imported: - Address %v - Keystore file: %v - -The key is now encrypted; losing the password will result in permanently losing -access to the key and all associated funds! - -Make sure to backup keystore and passwords in a safe location.`, - acc.Address, acc.URL.Path)) - return nil -} - -// ipcEndpoint resolves an IPC endpoint based on a configured value, taking into -// account the set data folders as well as the designated platform we're currently -// running on. -func ipcEndpoint(ipcPath, datadir string) string { - // On windows we can only use plain top-level pipes - if runtime.GOOS == "windows" { - if strings.HasPrefix(ipcPath, `\\.\pipe\`) { - return ipcPath - } - return `\\.\pipe\` + ipcPath - } - // Resolve names into the data directory full paths otherwise - if filepath.Base(ipcPath) == ipcPath { - if datadir == "" { - return filepath.Join(os.TempDir(), ipcPath) - } - return filepath.Join(datadir, ipcPath) - } - return ipcPath -} - -func signer(c *cli.Context) error { - // If we have some unrecognized command, bail out - if c.NArg() > 0 { - return fmt.Errorf("invalid command: %q", c.Args().First()) - } - if err := initialize(c); err != nil { - return err - } - var ( - ui core.UIClientAPI - ) - if c.Bool(stdiouiFlag.Name) { - log.Info("Using stdin/stdout as UI-channel") - ui = core.NewStdIOUI() - } else { - log.Info("Using CLI as UI-channel") - ui = core.NewCommandlineUI() - } - // 4bytedb data - fourByteLocal := c.String(customDBFlag.Name) - db, err := fourbyte.NewWithFile(fourByteLocal) - if err != nil { - utils.Fatalf(err.Error()) - } - embeds, locals := db.Size() - log.Info("Loaded 4byte database", "embeds", embeds, "locals", locals, "local", fourByteLocal) - - var ( - api core.ExternalAPI - pwStorage storage.Storage = &storage.NoStorage{} - ) - configDir := c.String(configdirFlag.Name) - if stretchedKey, err := readMasterKey(c, ui); err != nil { - log.Warn("Failed to open master, rules disabled", "err", err) - } else { - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10])) - - // Generate domain specific keys - pwkey := crypto.Keccak256([]byte("credentials"), stretchedKey) - jskey := crypto.Keccak256([]byte("jsstorage"), stretchedKey) - confkey := crypto.Keccak256([]byte("config"), stretchedKey) - - // Initialize the encrypted storages - pwStorage = storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey) - jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey) - configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey) - - // Do we have a rule-file? - if ruleFile := c.String(ruleFlag.Name); ruleFile != "" { - ruleJS, err := os.ReadFile(ruleFile) - if err != nil { - log.Warn("Could not load rules, disabling", "file", ruleFile, "err", err) - } else { - shasum := sha256.Sum256(ruleJS) - foundShaSum := hex.EncodeToString(shasum[:]) - storedShasum, _ := configStorage.Get("ruleset_sha256") - if storedShasum != foundShaSum { - log.Warn("Rule hash not attested, disabling", "hash", foundShaSum, "attested", storedShasum) - } else { - // Initialize rules - ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage) - if err != nil { - utils.Fatalf(err.Error()) - } - ruleEngine.Init(string(ruleJS)) - ui = ruleEngine - log.Info("Rule engine configured", "file", c.String(ruleFlag.Name)) - } - } - } - } - var ( - chainId = c.Int64(chainIdFlag.Name) - ksLoc = c.String(keystoreFlag.Name) - lightKdf = c.Bool(utils.LightKDFFlag.Name) - advanced = c.Bool(advancedMode.Name) - scpath = c.String(utils.SmartCardDaemonPathFlag.Name) - ) - log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc, - "light-kdf", lightKdf, "advanced", advanced) - am := core.StartClefAccountManager(ksLoc, lightKdf, scpath) - defer am.Close() - apiImpl := core.NewSignerAPI(am, chainId, ui, db, advanced, pwStorage) - - // Establish the bidirectional communication, by creating a new UI backend and registering - // it with the UI. - ui.RegisterUIServer(core.NewUIServerAPI(apiImpl)) - api = apiImpl - - // Audit logging - if logfile := c.String(auditLogFlag.Name); logfile != "" { - api, err = core.NewAuditLogger(logfile, api) - if err != nil { - utils.Fatalf(err.Error()) - } - log.Info("Audit logs configured", "file", logfile) - } - // register signer API with server - var ( - extapiURL = "n/a" - ipcapiURL = "n/a" - ) - rpcAPI := []rpc.API{ - { - Namespace: "account", - Service: api, - }, - } - if c.Bool(utils.HTTPEnabledFlag.Name) { - vhosts := utils.SplitAndTrim(c.String(utils.HTTPVirtualHostsFlag.Name)) - cors := utils.SplitAndTrim(c.String(utils.HTTPCORSDomainFlag.Name)) - - srv := rpc.NewServer() - srv.SetBatchLimits(node.DefaultConfig.BatchRequestLimit, node.DefaultConfig.BatchResponseMaxSize) - err := node.RegisterApis(rpcAPI, []string{"account"}, srv) - if err != nil { - utils.Fatalf("Could not register API: %w", err) - } - handler := node.NewHTTPHandlerStack(srv, cors, vhosts, nil, false) - - // set port - port := c.Int(rpcPortFlag.Name) - - // start http server - httpEndpoint := net.JoinHostPort(c.String(utils.HTTPListenAddrFlag.Name), fmt.Sprintf("%d", port)) - httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler) - if err != nil { - utils.Fatalf("Could not start RPC api: %v", err) - } - extapiURL = fmt.Sprintf("http://%v/", addr) - log.Info("HTTP endpoint opened", "url", extapiURL) - - defer func() { - // Don't bother imposing a timeout here. - httpServer.Shutdown(context.Background()) - log.Info("HTTP endpoint closed", "url", extapiURL) - }() - } - if !c.Bool(utils.IPCDisabledFlag.Name) { - givenPath := c.String(utils.IPCPathFlag.Name) - ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir) - listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI) - if err != nil { - utils.Fatalf("Could not start IPC api: %v", err) - } - log.Info("IPC endpoint opened", "url", ipcapiURL) - defer func() { - listener.Close() - log.Info("IPC endpoint closed", "url", ipcapiURL) - }() - } - if c.Bool(testFlag.Name) { - log.Info("Performing UI test") - go testExternalUI(apiImpl) - } - ui.OnSignerStartup(core.StartupInfo{ - Info: map[string]interface{}{ - "intapi_version": core.InternalAPIVersion, - "extapi_version": core.ExternalAPIVersion, - "extapi_http": extapiURL, - "extapi_ipc": ipcapiURL, - }}) - - abortChan := make(chan os.Signal, 1) - signal.Notify(abortChan, os.Interrupt) - - sig := <-abortChan - log.Info("Exiting...", "signal", sig) - - return nil -} - -// DefaultConfigDir is the default config directory to use for the vaults and other -// persistence requirements. -func DefaultConfigDir() string { - // Try to place the data folder in the user's home dir - home := flags.HomeDir() - if home != "" { - if runtime.GOOS == "darwin" { - return filepath.Join(home, "Library", "Signer") - } else if runtime.GOOS == "windows" { - appdata := os.Getenv("APPDATA") - if appdata != "" { - return filepath.Join(appdata, "Signer") - } - return filepath.Join(home, "AppData", "Roaming", "Signer") - } - return filepath.Join(home, ".clef") - } - // As we cannot guess a stable location, return empty and handle later - return "" -} - -func readMasterKey(ctx *cli.Context, ui core.UIClientAPI) ([]byte, error) { - var ( - file string - configDir = ctx.String(configdirFlag.Name) - ) - if ctx.IsSet(signerSecretFlag.Name) { - file = ctx.String(signerSecretFlag.Name) - } else { - file = filepath.Join(configDir, "masterseed.json") - } - if err := checkFile(file); err != nil { - return nil, err - } - cipherKey, err := os.ReadFile(file) - if err != nil { - return nil, err - } - var password string - // If ui is not nil, get the password from ui. - if ui != nil { - resp, err := ui.OnInputRequired(core.UserInputRequest{ - Title: "Master Password", - Prompt: "Please enter the password to decrypt the master seed", - IsPassword: true}) - if err != nil { - return nil, err - } - password = resp.Text - } else { - password = utils.GetPassPhrase("Decrypt master seed of clef", false) - } - masterSeed, err := decryptSeed(cipherKey, password) - if err != nil { - return nil, errors.New("failed to decrypt the master seed of clef") - } - if len(masterSeed) < 256 { - return nil, fmt.Errorf("master seed of insufficient length, expected >255 bytes, got %d", len(masterSeed)) - } - // Create vault location - vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), masterSeed)[:10])) - err = os.Mkdir(vaultLocation, 0700) - if err != nil && !os.IsExist(err) { - return nil, err - } - return masterSeed, nil -} - -// checkFile is a convenience function to check if a file -// * exists -// * is mode 0400 (unix only) -func checkFile(filename string) error { - info, err := os.Stat(filename) - if err != nil { - return fmt.Errorf("failed stat on %s: %v", filename, err) - } - // Check the unix permission bits - // However, on windows, we cannot use the unix perm-bits, see - // https://github.com/ethereum/go-ethereum/issues/20123 - if runtime.GOOS != "windows" && info.Mode().Perm()&0377 != 0 { - return fmt.Errorf("file (%v) has insecure file permissions (%v)", filename, info.Mode().String()) - } - return nil -} - -// confirm displays a text and asks for user confirmation -func confirm(text string) bool { - fmt.Print(text) - fmt.Printf("\nEnter 'ok' to proceed:\n> ") - - text, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text := strings.TrimSpace(text); text == "ok" { - return true - } - return false -} - -func testExternalUI(api *core.SignerAPI) { - ctx := context.WithValue(context.Background(), "remote", "clef binary") - ctx = context.WithValue(ctx, "scheme", "in-proc") - ctx = context.WithValue(ctx, "local", "main") - errs := make([]string, 0) - - a := common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef") - addErr := func(errStr string) { - log.Info("Test error", "err", errStr) - errs = append(errs, errStr) - } - - queryUser := func(q string) string { - resp, err := api.UI.OnInputRequired(core.UserInputRequest{ - Title: "Testing", - Prompt: q, - }) - if err != nil { - addErr(err.Error()) - } - return resp.Text - } - expectResponse := func(testcase, question, expect string) { - if got := queryUser(question); got != expect { - addErr(fmt.Sprintf("%s: got %v, expected %v", testcase, got, expect)) - } - } - expectApprove := func(testcase string, err error) { - if err == nil || err == accounts.ErrUnknownAccount { - return - } - addErr(fmt.Sprintf("%v: expected no error, got %v", testcase, err.Error())) - } - expectDeny := func(testcase string, err error) { - if err == nil || err != core.ErrRequestDenied { - addErr(fmt.Sprintf("%v: expected ErrRequestDenied, got %v", testcase, err)) - } - } - var delay = 1 * time.Second - // Test display of info and error - { - api.UI.ShowInfo("If you see this message, enter 'yes' to next question") - time.Sleep(delay) - expectResponse("showinfo", "Did you see the message? [yes/no]", "yes") - api.UI.ShowError("If you see this message, enter 'yes' to the next question") - time.Sleep(delay) - expectResponse("showerror", "Did you see the message? [yes/no]", "yes") - } - { // Sign data test - clique header - api.UI.ShowInfo("Please approve the next request for signing a clique header") - time.Sleep(delay) - cliqueHeader := types.Header{ - ParentHash: common.HexToHash("0000H45H"), - UncleHash: common.HexToHash("0000H45H"), - Coinbase: common.HexToAddress("0000H45H"), - Root: common.HexToHash("0000H00H"), - TxHash: common.HexToHash("0000H45H"), - ReceiptHash: common.HexToHash("0000H45H"), - Difficulty: big.NewInt(1337), - Number: big.NewInt(1337), - GasLimit: 1338, - GasUsed: 1338, - Time: 1338, - Extra: []byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"), - MixDigest: common.HexToHash("0x0000H45H"), - } - cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader) - if err != nil { - utils.Fatalf("Should not error: %v", err) - } - addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") - _, err = api.SignData(ctx, accounts.MimetypeClique, *addr, hexutil.Encode(cliqueRlp)) - expectApprove("signdata - clique header", err) - } - { // Sign data test - typed data - api.UI.ShowInfo("Please approve the next request for signing EIP-712 typed data") - time.Sleep(delay) - addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") - data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}` - //_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data))) - var typedData apitypes.TypedData - json.Unmarshal([]byte(data), &typedData) - _, err := api.SignTypedData(ctx, *addr, typedData) - expectApprove("sign 712 typed data", err) - } - { // Sign data test - plain text - api.UI.ShowInfo("Please approve the next request for signing text") - time.Sleep(delay) - addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") - _, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world"))) - expectApprove("signdata - text", err) - } - { // Sign data test - plain text reject - api.UI.ShowInfo("Please deny the next request for signing text") - time.Sleep(delay) - addr, _ := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899") - _, err := api.SignData(ctx, accounts.MimetypeTextPlain, *addr, hexutil.Encode([]byte("hello world"))) - expectDeny("signdata - text", err) - } - { // Sign transaction - api.UI.ShowInfo("Please reject next transaction") - time.Sleep(delay) - data := hexutil.Bytes([]byte{}) - to := common.NewMixedcaseAddress(a) - tx := apitypes.SendTxArgs{ - Data: &data, - Nonce: 0x1, - Value: hexutil.Big(*big.NewInt(6)), - From: common.NewMixedcaseAddress(a), - To: &to, - GasPrice: (*hexutil.Big)(big.NewInt(5)), - Gas: 1000, - Input: nil, - } - _, err := api.SignTransaction(ctx, tx, nil) - expectDeny("signtransaction [1]", err) - expectResponse("signtransaction [2]", "Did you see any warnings for the last transaction? (yes/no)", "no") - } - { // Listing - api.UI.ShowInfo("Please reject listing-request") - time.Sleep(delay) - _, err := api.List(ctx) - expectDeny("list", err) - } - { // Import - api.UI.ShowInfo("Please reject new account-request") - time.Sleep(delay) - _, err := api.New(ctx) - expectDeny("newaccount", err) - } - { // Metadata - api.UI.ShowInfo("Please check if you see the Origin in next listing (approve or deny)") - time.Sleep(delay) - api.List(context.WithValue(ctx, "Origin", "origin.com")) - expectResponse("metadata - origin", "Did you see origin (origin.com)? [yes/no] ", "yes") - } - - for _, e := range errs { - log.Error(e) - } - result := fmt.Sprintf("Tests completed. %d errors:\n%s\n", len(errs), strings.Join(errs, "\n")) - api.UI.ShowInfo(result) -} - -type encryptedSeedStorage struct { - Description string `json:"description"` - Version int `json:"version"` - Params keystore.CryptoJSON `json:"params"` -} - -// encryptSeed uses a similar scheme as the keystore uses, but with a different wrapping, -// to encrypt the master seed -func encryptSeed(seed []byte, auth []byte, scryptN, scryptP int) ([]byte, error) { - cryptoStruct, err := keystore.EncryptDataV3(seed, auth, scryptN, scryptP) - if err != nil { - return nil, err - } - return json.Marshal(&encryptedSeedStorage{"Clef seed", 1, cryptoStruct}) -} - -// decryptSeed decrypts the master seed -func decryptSeed(keyjson []byte, auth string) ([]byte, error) { - var encSeed encryptedSeedStorage - if err := json.Unmarshal(keyjson, &encSeed); err != nil { - return nil, err - } - if encSeed.Version != 1 { - log.Warn(fmt.Sprintf("unsupported encryption format of seed: %d, operation will likely fail", encSeed.Version)) - } - seed, err := keystore.DecryptDataV3(encSeed.Params, auth) - if err != nil { - return nil, err - } - return seed, err -} - -// GenDoc outputs examples of all structures used in json-rpc communication -func GenDoc(ctx *cli.Context) error { - var ( - a = common.HexToAddress("0xdeadbeef000000000000000000000000deadbeef") - b = common.HexToAddress("0x1111111122222222222233333333334444444444") - meta = core.Metadata{ - Scheme: "http", - Local: "localhost:8545", - Origin: "www.malicious.ru", - Remote: "localhost:9999", - UserAgent: "Firefox 3.2", - } - output []string - add = func(name, desc string, v interface{}) { - if data, err := json.MarshalIndent(v, "", " "); err == nil { - output = append(output, fmt.Sprintf("### %s\n\n%s\n\nExample:\n```json\n%s\n```", name, desc, data)) - } else { - log.Error("Error generating output", "err", err) - } - } - ) - - { // Sign plain text request - desc := "SignDataRequest contains information about a pending request to sign some data. " + - "The data to be signed can be of various types, defined by content-type. Clef has done most " + - "of the work in canonicalizing and making sense of the data, and it's up to the UI to present" + - "the user with the contents of the `message`" - sighash, msg := accounts.TextAndHash([]byte("hello world")) - messages := []*apitypes.NameValueType{{Name: "message", Value: msg, Typ: accounts.MimetypeTextPlain}} - - add("SignDataRequest", desc, &core.SignDataRequest{ - Address: common.NewMixedcaseAddress(a), - Meta: meta, - ContentType: accounts.MimetypeTextPlain, - Rawdata: []byte(msg), - Messages: messages, - Hash: sighash}) - } - { // Sign plain text response - add("SignDataResponse - approve", "Response to SignDataRequest", - &core.SignDataResponse{Approved: true}) - add("SignDataResponse - deny", "Response to SignDataRequest", - &core.SignDataResponse{}) - } - { // Sign transaction request - desc := "SignTxRequest contains information about a pending request to sign a transaction. " + - "Aside from the transaction itself, there is also a `call_info`-struct. That struct contains " + - "messages of various types, that the user should be informed of." + - "\n\n" + - "As in any request, it's important to consider that the `meta` info also contains untrusted data." + - "\n\n" + - "The `transaction` (on input into clef) can have either `data` or `input` -- if both are set, " + - "they must be identical, otherwise an error is generated. " + - "However, Clef will always use `data` when passing this struct on (if Clef does otherwise, please file a ticket)" - - data := hexutil.Bytes([]byte{0x01, 0x02, 0x03, 0x04}) - add("SignTxRequest", desc, &core.SignTxRequest{ - Meta: meta, - Callinfo: []apitypes.ValidationInfo{ - {Typ: "Warning", Message: "Something looks odd, show this message as a warning"}, - {Typ: "Info", Message: "User should see this as well"}, - }, - Transaction: apitypes.SendTxArgs{ - Data: &data, - Nonce: 0x1, - Value: hexutil.Big(*big.NewInt(6)), - From: common.NewMixedcaseAddress(a), - To: nil, - GasPrice: (*hexutil.Big)(big.NewInt(5)), - Gas: 1000, - Input: nil, - }}) - } - { // Sign tx response - data := hexutil.Bytes([]byte{0x04, 0x03, 0x02, 0x01}) - add("SignTxResponse - approve", "Response to request to sign a transaction. This response needs to contain the `transaction`"+ - ", because the UI is free to make modifications to the transaction.", - &core.SignTxResponse{Approved: true, - Transaction: apitypes.SendTxArgs{ - Data: &data, - Nonce: 0x4, - Value: hexutil.Big(*big.NewInt(6)), - From: common.NewMixedcaseAddress(a), - To: nil, - GasPrice: (*hexutil.Big)(big.NewInt(5)), - Gas: 1000, - Input: nil, - }}) - add("SignTxResponse - deny", "Response to SignTxRequest. When denying a request, there's no need to "+ - "provide the transaction in return", - &core.SignTxResponse{}) - } - { // WHen a signed tx is ready to go out - desc := "SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)`" + - "\n\n" + - "This occurs _after_ successful completion of the entire signing procedure, but right before the signed " + - "transaction is passed to the external caller. This method (and data) can be used by the UI to signal " + - "to the user that the transaction was signed, but it is primarily useful for ruleset implementations." + - "\n\n" + - "A ruleset that implements a rate limitation needs to know what transactions are sent out to the external " + - "interface. By hooking into this methods, the ruleset can maintain track of that count." + - "\n\n" + - "**OBS:** Note that if an attacker can restore your `clef` data to a previous point in time" + - " (e.g through a backup), the attacker can reset such windows, even if he/she is unable to decrypt the content. " + - "\n\n" + - "The `OnApproved` method cannot be responded to, it's purely informative" - - rlpdata := common.FromHex("0xf85d640101948a8eafb1cf62bfbeb1741769dae1a9dd47996192018026a0716bd90515acb1e68e5ac5867aa11a1e65399c3349d479f5fb698554ebc6f293a04e8a4ebfff434e971e0ef12c5bf3a881b06fd04fc3f8b8a7291fb67a26a1d4ed") - var tx types.Transaction - tx.UnmarshalBinary(rlpdata) - add("OnApproved - SignTransactionResult", desc, ðapi.SignTransactionResult{Raw: rlpdata, Tx: &tx}) - } - { // User input - add("UserInputRequest", "Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free)", - &core.UserInputRequest{IsPassword: true, Title: "The title here", Prompt: "The question to ask the user"}) - add("UserInputResponse", "Response to UserInputRequest", - &core.UserInputResponse{Text: "The textual response from user"}) - } - { // List request - add("ListRequest", "Sent when a request has been made to list addresses. The UI is provided with the "+ - "full `account`s, including local directory names. Note: this information is not passed back to the external caller, "+ - "who only sees the `address`es. ", - &core.ListRequest{ - Meta: meta, - Accounts: []accounts.Account{ - {Address: a, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/a"}}, - {Address: b, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}}, - }) - - add("ListResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+ - "Note: the UI is free to respond with any address the caller, regardless of whether it exists or not", - &core.ListResponse{ - Accounts: []accounts.Account{ - { - Address: common.HexToAddress("0xcowbeef000000cowbeef00000000000000000c0w"), - URL: accounts.URL{Path: ".. ignored .."}, - }, - { - Address: common.MaxAddress, - }, - }}) - } - - fmt.Println(`## UI Client interface - -These data types are defined in the channel between clef and the UI`) - for _, elem := range output { - fmt.Println(elem) - } - return nil -} diff --git a/cmd/clef/pythonsigner.py b/cmd/clef/pythonsigner.py deleted file mode 100644 index 5d0eb18dcc..0000000000 --- a/cmd/clef/pythonsigner.py +++ /dev/null @@ -1,315 +0,0 @@ -import sys -import subprocess - -from tinyrpc.transports import ServerTransport -from tinyrpc.protocols.jsonrpc import JSONRPCProtocol -from tinyrpc.dispatch import public, RPCDispatcher -from tinyrpc.server import RPCServer - -""" -This is a POC example of how to write a custom UI for Clef. -The UI starts the clef process with the '--stdio-ui' option -and communicates with clef using standard input / output. - -The standard input/output is a relatively secure way to communicate, -as it does not require opening any ports or IPC files. Needless to say, -it does not protect against memory inspection mechanisms -where an attacker can access process memory. - -To make this work install all the requirements: - - pip install -r requirements.txt -""" - -try: - import urllib.parse as urlparse -except ImportError: - import urllib as urlparse - - -class StdIOTransport(ServerTransport): - """Uses std input/output for RPC""" - - def receive_message(self): - return None, urlparse.unquote(sys.stdin.readline()) - - def send_reply(self, context, reply): - print(reply) - - -class PipeTransport(ServerTransport): - """Uses std a pipe for RPC""" - - def __init__(self, input, output): - self.input = input - self.output = output - - def receive_message(self): - data = self.input.readline() - print(">> {}".format(data)) - return None, urlparse.unquote(data) - - def send_reply(self, context, reply): - reply = str(reply, "utf-8") - print("<< {}".format(reply)) - self.output.write("{}\n".format(reply)) - - -def sanitize(txt, limit=100): - return txt[:limit].encode("unicode_escape").decode("utf-8") - - -def metaString(meta): - """ - "meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""} - """ # noqa: E501 - message = ( - "\tRequest context:\n" - "\t\t{remote} -> {scheme} -> {local}\n" - "\tAdditional HTTP header data, provided by the external caller:\n" - "\t\tUser-Agent: {user_agent}\n" - "\t\tOrigin: {origin}\n" - ) - return message.format( - remote=meta.get("remote", ""), - scheme=meta.get("scheme", ""), - local=meta.get("local", ""), - user_agent=sanitize(meta.get("User-Agent"), 200), - origin=sanitize(meta.get("Origin"), 100), - ) - - -class StdIOHandler: - def __init__(self): - pass - - @public - def approveTx(self, req): - """ - Example request: - - {"jsonrpc":"2.0","id":20,"method":"ui_approveTx","params":[{"transaction":{"from":"0xDEADbEeF000000000000000000000000DeaDbeEf","to":"0xDEADbEeF000000000000000000000000DeaDbeEf","gas":"0x3e8","gasPrice":"0x5","maxFeePerGas":null,"maxPriorityFeePerGas":null,"value":"0x6","nonce":"0x1","data":"0x"},"call_info":null,"meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} - - :param transaction: transaction info - :param call_info: info about the call, e.g. if ABI info could not be - :param meta: metadata about the request, e.g. where the call comes from - :return: - """ # noqa: E501 - message = ( - "Sign transaction request:\n" - "\t{meta_string}\n" - "\n" - "\tFrom: {from_}\n" - "\tTo: {to}\n" - "\n" - "\tAuto-rejecting request" - ) - meta = req.get("meta", {}) - transaction = req.get("transaction") - sys.stdout.write( - message.format( - meta_string=metaString(meta), - from_=transaction.get("from", ""), - to=transaction.get("to", ""), - ) - ) - return { - "approved": False, - } - - @public - def approveSignData(self, req): - """ - Example request: - - {"jsonrpc":"2.0","id":8,"method":"ui_approveSignData","params":[{"content_type":"application/x-clique-header","address":"0x0011223344556677889900112233445566778899","raw_data":"+QIRoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIIFOYIFOYIFOoIFOoIFOppFeHRyYSBkYXRhIEV4dHJhIGRhdGEgRXh0cqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAA==","messages":[{"name":"Clique header","value":"clique header 1337 [0x44381ab449d77774874aca34634cb53bc21bd22aef2d3d4cf40e51176cb585ec]","type":"clique"}],"call_info":null,"hash":"0xa47ab61438a12a06c81420e308c2b7aae44e9cd837a5df70dd021421c0f58643","meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} - """ # noqa: E501 - message = ( - "Sign data request:\n" - "\t{meta_string}\n" - "\n" - "\tContent-type: {content_type}\n" - "\tAddress: {address}\n" - "\tHash: {hash_}\n" - "\n" - "\tAuto-rejecting request\n" - ) - meta = req.get("meta", {}) - sys.stdout.write( - message.format( - meta_string=metaString(meta), - content_type=req.get("content_type"), - address=req.get("address"), - hash_=req.get("hash"), - ) - ) - - return { - "approved": False, - "password": None, - } - - @public - def approveNewAccount(self, req): - """ - Example request: - - {"jsonrpc":"2.0","id":25,"method":"ui_approveNewAccount","params":[{"meta":{"remote":"clef binary","local":"main","scheme":"in-proc","User-Agent":"","Origin":""}}]} - """ # noqa: E501 - message = ( - "Create new account request:\n" - "\t{meta_string}\n" - "\n" - "\tAuto-rejecting request\n" - ) - meta = req.get("meta", {}) - sys.stdout.write(message.format(meta_string=metaString(meta))) - return { - "approved": False, - } - - @public - def showError(self, req): - """ - Example request: - - {"jsonrpc":"2.0","method":"ui_showError","params":[{"text":"If you see this message, enter 'yes' to the next question"}]} - - :param message: to display - :return:nothing - """ # noqa: E501 - message = ( - "## Error\n{text}\n" - "Press enter to continue\n" - ) - text = req.get("text") - sys.stdout.write(message.format(text=text)) - input() - return - - @public - def showInfo(self, req): - """ - Example request: - - {"jsonrpc":"2.0","method":"ui_showInfo","params":[{"text":"If you see this message, enter 'yes' to next question"}]} - - :param message: to display - :return:nothing - """ # noqa: E501 - message = ( - "## Info\n{text}\n" - "Press enter to continue\n" - ) - text = req.get("text") - sys.stdout.write(message.format(text=text)) - input() - return - - @public - def onSignerStartup(self, req): - """ - Example request: - - {"jsonrpc":"2.0", "method":"ui_onSignerStartup", "params":[{"info":{"extapi_http":"n/a","extapi_ipc":"/home/user/.clef/clef.ipc","extapi_version":"6.1.0","intapi_version":"7.0.1"}}]} - """ # noqa: E501 - message = ( - "\n" - "\t\tExt api url: {extapi_http}\n" - "\t\tInt api ipc: {extapi_ipc}\n" - "\t\tExt api ver: {extapi_version}\n" - "\t\tInt api ver: {intapi_version}\n" - ) - info = req.get("info") - sys.stdout.write( - message.format( - extapi_http=info.get("extapi_http"), - extapi_ipc=info.get("extapi_ipc"), - extapi_version=info.get("extapi_version"), - intapi_version=info.get("intapi_version"), - ) - ) - - @public - def approveListing(self, req): - """ - Example request: - - {"jsonrpc":"2.0","id":23,"method":"ui_approveListing","params":[{"accounts":[{"address":... - """ # noqa: E501 - message = ( - "\n" - "## Account listing request\n" - "\t{meta_string}\n" - "\tDo you want to allow listing the following accounts?\n" - "\t-{addrs}\n" - "\n" - "->Auto-answering No\n" - ) - meta = req.get("meta", {}) - accounts = req.get("accounts", []) - addrs = [x.get("address") for x in accounts] - sys.stdout.write( - message.format( - addrs="\n\t-".join(addrs), - meta_string=metaString(meta) - ) - ) - return {} - - @public - def onInputRequired(self, req): - """ - Example request: - - {"jsonrpc":"2.0","id":1,"method":"ui_onInputRequired","params":[{"title":"Master Password","prompt":"Please enter the password to decrypt the master seed","isPassword":true}]} - - :param message: to display - :return:nothing - """ # noqa: E501 - message = ( - "\n" - "## {title}\n" - "\t{prompt}\n" - "\n" - "> " - ) - sys.stdout.write( - message.format( - title=req.get("title"), - prompt=req.get("prompt") - ) - ) - isPassword = req.get("isPassword") - if not isPassword: - return {"text": input()} - - return "" - - -def main(args): - cmd = ["clef", "--stdio-ui"] - if len(args) > 0 and args[0] == "test": - cmd.extend(["--stdio-ui-test"]) - print("cmd: {}".format(" ".join(cmd))) - - dispatcher = RPCDispatcher() - dispatcher.register_instance(StdIOHandler(), "ui_") - - # line buffered - p = subprocess.Popen( - cmd, - bufsize=1, - universal_newlines=True, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - ) - - rpc_server = RPCServer( - PipeTransport(p.stdout, p.stdin), JSONRPCProtocol(), dispatcher - ) - rpc_server.serve_forever() - - -if __name__ == "__main__": - main(sys.argv[1:]) diff --git a/cmd/clef/requirements.txt b/cmd/clef/requirements.txt deleted file mode 100644 index 5381862e30..0000000000 --- a/cmd/clef/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -tinyrpc==1.1.4 diff --git a/cmd/clef/rules.md b/cmd/clef/rules.md deleted file mode 100644 index ef89ec00c8..0000000000 --- a/cmd/clef/rules.md +++ /dev/null @@ -1,234 +0,0 @@ -# Rules - -The `signer` binary contains a ruleset engine, implemented with [OttoVM](https://github.com/robertkrimen/otto) - -It enables use cases like the following: - -* I want to auto-approve transactions with contract `CasinoDapp`, with up to `0.05 ether` in value to maximum `1 ether` per 24h period -* I want to auto-approve transaction to contract `EthAlarmClock` with `data`=`0xdeadbeef`, if `value=0`, `gas < 44k` and `gasPrice < 40Gwei` - -The two main features that are required for this to work well are: - -1. Rule Implementation: how to create, manage, and interpret rules in a flexible but secure manner -2. Credential management and credentials; how to provide auto-unlock without exposing keys unnecessarily. - -The section below deals with both of them - -## Rule Implementation - -A ruleset file is implemented as a `js` file. Under the hood, the ruleset engine is a `SignerUI`, implementing the same methods as the `json-rpc` methods -defined in the UI protocol. Example: - -```js -function asBig(str) { - if (str.slice(0, 2) == "0x") { - return new BigNumber(str.slice(2), 16) - } - return new BigNumber(str) -} - -// Approve transactions to a certain contract if the value is below a certain limit -function ApproveTx(req) { - var limit = new BigNumber("0xb1a2bc2ec50000") - var value = asBig(req.transaction.value); - - if (req.transaction.to.toLowerCase() == "0xae967917c465db8578ca9024c205720b1a3651a9" && value.lt(limit)) { - return "Approve" - } - // If we return "Reject", it will be rejected. - // By not returning anything, it will be passed to the next UI, for manual processing -} - -// Approve listings if request made from IPC -function ApproveListing(req){ - if (req.metadata.scheme == "ipc"){ return "Approve"} -} -``` - -Whenever the external API is called (and the ruleset is enabled), the `signer` calls the UI, which is an instance of a ruleset-engine. The ruleset-engine -invokes the corresponding method. In doing so, there are three possible outcomes: - -1. JS returns "Approve" - * Auto-approve request -2. JS returns "Reject" - * Auto-reject request -3. Error occurs, or something else is returned - * Pass on to `next` ui: the regular UI channel. - -A more advanced example can be found below, "Example 1: ruleset for a rate-limited window", using `storage` to `Put` and `Get` `string`s by key. - -* At the time of writing, storage only exists as an ephemeral unencrypted implementation, to be used during testing. - -### Things to note - -The Otto vm has a few [caveats](https://github.com/robertkrimen/otto): - -* "use strict" will parse, but does nothing. -* The regular expression engine (re2/regexp) is not fully compatible with the ECMA5 specification. -* Otto targets ES5. ES6 features (eg: Typed Arrays) are not supported. - -Additionally, a few more have been added - -* The rule execution cannot load external javascript files. -* The only preloaded library is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the GitHub repository. -* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data. -* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes. -* The JS engine has access to `storage` and `console`. - -#### Security considerations - -##### Security of ruleset - -Some security precautions can be made, such as: - -* Never load `ruleset.js` unless the file is `readonly` (`r-??-??-?`). If the user wishes to modify the ruleset, he must make it writeable and then set back to readonly. - * This is to prevent attacks where files are dropped on the users disk. -* Since we're going to have to have some form of secure storage (not defined in this section), we could also store the `sha3` of the `ruleset.js` file in there. - * If the user wishes to modify the ruleset, he'd then have to perform e.g. `signer --attest /path/to/ruleset --credential ` - -##### Security of implementation - -The drawback of this very flexible solution is that the `signer` needs to contain a javascript engine. This is pretty simple to implement since it's already -implemented for `geth`. There are no known security vulnerabilities in it, nor have we had any security problems with it so far. - -The javascript engine would be an added attack surface; but if the validation of `rulesets` is made good (with hash-based attestation), the actual javascript cannot be considered -an attack surface -- if an attacker can control the ruleset, a much simpler attack would be to implement an "always-approve" rule instead of exploiting the js vm. The only benefit -to be gained from attacking the actual `signer` process from the `js` side would be if it could somehow extract cryptographic keys from memory. - -##### Security in usability - -Javascript is flexible, but also easy to get wrong, especially when users assume that `js` can handle large integers natively. Typical errors -include trying to multiply `gasCost` with `gas` without using `bigint`:s. - -It's unclear whether any other DSL could be more secure; since there's always the possibility of erroneously implementing a rule. - - -## Credential management - -The ability to auto-approve transactions means that the signer needs to have the necessary credentials to decrypt keyfiles. These passwords are hereafter called `ksp` (keystore pass). - -### Example implementation - -Upon startup of the signer, the signer is given a switch: `--seed ` -The `seed` contains a blob of bytes, which is the master seed for the `signer`. - -The `signer` uses the `seed` to: - -* Generate the `path` where the settings are stored. - * `./settings/1df094eb-c2b1-4689-90dd-790046d38025/vault.dat` - * `./settings/1df094eb-c2b1-4689-90dd-790046d38025/rules.js` -* Generate the encryption password for `vault.dat`. - -The `vault.dat` would be an encrypted container storing the following information: - -* `ksp` entries -* `sha256` hash of `rules.js` -* Information about pair:ed callers (not yet specified) - -### Security considerations - -This would leave it up to the user to ensure that the `path/to/masterseed` is handled securely. It's difficult to get around this, although one could -imagine leveraging OS-level keychains where supported. The setup is however, in general, similar to how ssh-keys are stored in `.ssh/`. - - -# Implementation status - -This is now implemented (with ephemeral non-encrypted storage for now, so not yet enabled). - -## Example 1: ruleset for a rate-limited window - - -```js -function big(str) { - if (str.slice(0, 2) == "0x") { - return new BigNumber(str.slice(2), 16) - } - return new BigNumber(str) -} - -// Time window: 1 week -var window = 1000* 3600*24*7; - -// Limit: 1 ether -var limit = new BigNumber("1e18"); - -function isLimitOk(transaction) { - var value = big(transaction.value) - // Start of our window function - var windowstart = new Date().getTime() - window; - - var txs = []; - var stored = storage.get('txs'); - - if (stored != "") { - txs = JSON.parse(stored) - } - // First, remove all that has passed out of the time window - var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart}); - console.log(txs, newtxs.length); - - // Secondly, aggregate the current sum - sum = new BigNumber(0) - - sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum); - console.log("ApproveTx > Sum so far", sum); - console.log("ApproveTx > Requested", value.toNumber()); - - // Would we exceed the weekly limit ? - return sum.plus(value).lt(limit) - -} -function ApproveTx(r) { - if (isLimitOk(r.transaction)) { - return "Approve" - } - return "Nope" -} - -/** -* OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter - * 'response_str' contains the return value that will be sent to the external caller. -* The return value from this method is ignore - the reason for having this callback is to allow the -* ruleset to keep track of approved transactions. -* -* When implementing rate-limited rules, this callback should be used. -* If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user -* then accepts the transaction, this method will be called. -* -* TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx. -*/ -function OnApprovedTx(resp) { - var value = big(resp.tx.value) - var txs = [] - // Load stored transactions - var stored = storage.get('txs'); - if (stored != "") { - txs = JSON.parse(stored) - } - // Add this to the storage - txs.push({tstamp: new Date().getTime(), value: value}); - storage.put("txs", JSON.stringify(txs)); -} -``` - -## Example 2: allow destination - -```js -function ApproveTx(r) { - if (r.transaction.from.toLowerCase() == "0x0000000000000000000000000000000000001337") { - return "Approve" - } - if (r.transaction.from.toLowerCase() == "0x000000000000000000000000000000000000dead") { - return "Reject" - } - // Otherwise goes to manual processing -} -``` - -## Example 3: Allow listing - -```js -function ApproveListing() { - return "Approve" -} -``` diff --git a/cmd/clef/run_test.go b/cmd/clef/run_test.go deleted file mode 100644 index d404457ba2..0000000000 --- a/cmd/clef/run_test.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2022 The go-ethereum Authors -// This file is part of go-ethereum. -// -// go-ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// go-ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with go-ethereum. If not, see . - -package main - -import ( - "fmt" - "os" - "testing" - - "github.com/ethereum/go-ethereum/internal/cmdtest" - "github.com/ethereum/go-ethereum/internal/reexec" -) - -const registeredName = "clef-test" - -type testproc struct { - *cmdtest.TestCmd - - // template variables for expect - Datadir string - Etherbase string -} - -func init() { - reexec.Register(registeredName, func() { - if err := app.Run(os.Args); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - os.Exit(0) - }) -} - -func TestMain(m *testing.M) { - // check if we have been reexec'd - if reexec.Init() { - return - } - os.Exit(m.Run()) -} - -// runClef spawns clef with the given command line args and adds keystore arg. -// This method creates a temporary keystore folder which will be removed after -// the test exits. -func runClef(t *testing.T, args ...string) *testproc { - ddir := t.TempDir() - return runWithKeystore(t, ddir, args...) -} - -// runWithKeystore spawns clef with the given command line args and adds keystore arg. -// This method does _not_ create the keystore folder, but it _does_ add the arg -// to the args. -func runWithKeystore(t *testing.T, keystore string, args ...string) *testproc { - args = append([]string{"--keystore", keystore}, args...) - tt := &testproc{Datadir: keystore} - tt.TestCmd = cmdtest.NewTestCmd(t, tt) - // Boot "clef". This actually runs the test binary but the TestMain - // function will prevent any tests from running. - tt.Run(registeredName, args...) - return tt -} - -func (proc *testproc) input(text string) *testproc { - proc.TestCmd.InputLine(text) - return proc -} - -/* -// waitForEndpoint waits for the rpc endpoint to appear, or -// aborts after 3 seconds. -func (proc *testproc) waitForEndpoint(t *testing.T) *testproc { - t.Helper() - timeout := 3 * time.Second - ipc := filepath.Join(proc.Datadir, "clef.ipc") - - start := time.Now() - for time.Since(start) < timeout { - if _, err := os.Stat(ipc); !errors.Is(err, os.ErrNotExist) { - t.Logf("endpoint %v opened", ipc) - return proc - } - time.Sleep(200 * time.Millisecond) - } - t.Logf("stderr: \n%v", proc.StderrText()) - t.Logf("stdout: \n%v", proc.Output()) - t.Fatal("endpoint", ipc, "did not open within", timeout) - return proc -} -*/ diff --git a/cmd/clef/sign_flow.png b/cmd/clef/sign_flow.png deleted file mode 100644 index e7010ab43f3a6f54d69958055b93b611f7853951..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20537 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. - -// This file is a test-utility for testing clef-functionality -// -// Start clef with -// -// build/bin/clef --4bytedb=./cmd/clef/4byte.json --rpc -// -// Start geth with -// -// build/bin/geth --nodiscover --maxpeers 0 --signer http://localhost:8550 console --preload=cmd/clef/tests/testsigner.js -// -// and in the console simply invoke -// -// > test() -// -// You can reload the file via `reload()` - -function reload(){ - loadScript("./cmd/clef/tests/testsigner.js"); -} - -function init(){ - if (typeof accts == 'undefined' || accts.length == 0){ - accts = eth.accounts - console.log("Got accounts ", accts); - } -} -init() -function testTx(){ - if( accts && accts.length > 0) { - var a = accts[0] - var txdata = eth.signTransaction({from: a, to: a, value: 1, nonce: 1, gas: 1, gasPrice: 1}) - var v = parseInt(txdata.tx.v) - console.log("V value: ", v) - if (v == 37 || v == 38){ - console.log("Mainnet 155-protected chainid was used") - } - if (v == 27 || v == 28){ - throw new Error("Mainnet chainid was used, but without replay protection!") - } - } -} -function testSignText(){ - if( accts && accts.length > 0){ - var a = accts[0] - var r = eth.sign(a, "0x68656c6c6f20776f726c64"); //hello world - console.log("signing response", r) - } -} -function testClique(){ - if( accts && accts.length > 0){ - var a = accts[0] - var r = debug.testSignCliqueBlock(a, 0); // Sign genesis - console.log("signing response", r) - if( a != r){ - throw new Error("Requested signing by "+a+ " but got sealer "+r) - } - } -} - -function test(){ - var tests = [ - testTx, - testSignText, - testClique, - ] - for( i in tests){ - try{ - tests[i]() - }catch(err){ - console.log(err) - } - } - } diff --git a/cmd/clef/tutorial.md b/cmd/clef/tutorial.md deleted file mode 100644 index 3ea662b5d4..0000000000 --- a/cmd/clef/tutorial.md +++ /dev/null @@ -1,353 +0,0 @@ -## Initializing Clef - -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: - -```text -$ clef init - -WARNING! - -Clef is an account management tool. It may, like any software, contain bugs. - -Please take care to -- backup your keystore files, -- verify that the keystore(s) can be opened with your password. - -Clef is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the GNU General Public License for more details. - -Enter 'ok' to proceed: -> ok - -The master seed of clef will be locked with a password. -Please specify a password. Do not forget this password! -Password: -Repeat password: - -A master seed has been generated into /home/martin/.clef/masterseed.json - -This is required to be able to store credentials, such as: -* Passwords for keystores (used by rule engine) -* Storage for JavaScript auto-signing rules -* Hash of JavaScript rule-file - -You should treat 'masterseed.json' with utmost secrecy and make a backup of it! -* The password is necessary but not enough, you need to back up the master seed too! -* The master seed does not contain your accounts, those need to be backed up separately! -``` - -*For readability purposes, we'll remove the WARNING printout, user confirmation and the unlocking of the master seed in the rest of this document.* - -## Remote interactions - -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). - -```text -$ clef --keystore ~/.ethereum/rinkeby/keystore --chainid 4 - -INFO [07-01|11:00:46.385] Starting signer chainid=4 keystore=$HOME/.ethereum/rinkeby/keystore light-kdf=false advanced=false -DEBUG[07-01|11:00:46.389] FS scan times list=3.521941ms set=9.017µs diff=4.112µs -DEBUG[07-01|11:00:46.391] Ledger support enabled -DEBUG[07-01|11:00:46.391] Trezor support enabled via HID -DEBUG[07-01|11:00:46.391] Trezor support enabled via WebUSB -INFO [07-01|11:00:46.391] Audit logs configured file=audit.log -DEBUG[07-01|11:00:46.392] IPC registered namespace=account -INFO [07-01|11:00:46.392] IPC endpoint opened url=$HOME/.clef/clef.ipc -------- Signer info ------- -* intapi_version : 7.0.0 -* extapi_version : 6.0.0 -* extapi_http : n/a -* extapi_ipc : $HOME/.clef/clef.ipc -``` - -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*. - -To test this out, we can *request* Clef to list all account via its *External API endpoint*: - -```text -echo '{"id": 1, "jsonrpc": "2.0", "method": "account_list"}' | nc -U ~/.clef/clef.ipc -``` - -This will prompt the user within the Clef CLI to confirm or deny the request: - -```text --------- List Account request-------------- -A request has been made to list all accounts. -You can select which accounts the caller can see - [x] 0xD9C9Cd5f6779558b6e0eD4e6Acf6b1947E7fA1F3 - URL: keystore://$HOME/.ethereum/rinkeby/keystore/UTC--2017-04-14T15-15-00.327614556Z--d9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3 - [x] 0x086278A6C067775F71d6B2BB1856Db6E28c30418 - URL: keystore://$HOME/.ethereum/rinkeby/keystore/UTC--2018-02-06T22-53-11.211657239Z--086278a6c067775f71d6b2bb1856db6e28c30418 -------------------------------------------- -Request context: - NA -> NA -> NA - -Additional HTTP header data, provided by the external caller: - User-Agent: - Origin: -Approve? [y/N]: -> -``` - -Depending on whether we approve or deny the request, the original NetCat process will get: - -```text -{"jsonrpc":"2.0","id":1,"result":["0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3","0x086278a6c067775f71d6b2bb1856db6e28c30418"]} - -or - -{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Request denied"}} -``` - -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/ethereum/go-ethereum/tree/master/cmd/clef#external-api-1) and the [External API Changelog](https://github.com/ethereum/go-ethereum/blob/master/cmd/clef/extapi_changelog.md). - -*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/ethereum/go-ethereum/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.* - -## Automatic rules - -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. - -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: - -```js -function ApproveListing() { - return "Approve" -} -``` - -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. - -```text -$ sha256sum rules.js -645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c rules.js - -$ clef attest 645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c -Decrypt master seed of clef -Password: -INFO [07-01|13:25:03.290] Ruleset attestation updated sha256=645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c -``` - -At this point, we can start Clef with the rule file: - -```text -$ clef --keystore ~/.ethereum/rinkeby/keystore --chainid 4 --rules rules.js - -INFO [07-01|13:39:49.726] Rule engine configured file=rules.js -INFO [07-01|13:39:49.726] Starting signer chainid=4 keystore=$HOME/.ethereum/rinkeby/keystore light-kdf=false advanced=false -DEBUG[07-01|13:39:49.726] FS scan times list=35.15µs set=4.251µs diff=2.766µs -DEBUG[07-01|13:39:49.727] Ledger support enabled -DEBUG[07-01|13:39:49.727] Trezor support enabled via HID -DEBUG[07-01|13:39:49.727] Trezor support enabled via WebUSB -INFO [07-01|13:39:49.728] Audit logs configured file=audit.log -DEBUG[07-01|13:39:49.728] IPC registered namespace=account -INFO [07-01|13:39:49.728] IPC endpoint opened url=$HOME/.clef/clef.ipc -------- Signer info ------- -* intapi_version : 7.0.0 -* extapi_version : 6.0.0 -* extapi_http : n/a -* extapi_ipc : $HOME/.clef/clef.ipc -``` - -Any account listing *request* will now be auto-approved by the rule file: - -```text -$ echo '{"id": 1, "jsonrpc": "2.0", "method": "account_list"}' | nc -U ~/.clef/clef.ipc -{"jsonrpc":"2.0","id":1,"result":["0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3","0x086278a6c067775f71d6b2bb1856db6e28c30418"]} -``` - -## Under the hood - -While doing the operations above, these files have been created: - -```text -$ ls -laR ~/.clef/ - -$HOME/.clef/: -total 24 -drwxr-x--x 3 user user 4096 Jul 1 13:45 . -drwxr-xr-x 102 user user 12288 Jul 1 13:39 .. -drwx------ 2 user user 4096 Jul 1 13:25 02f90c0603f4f2f60188 --r-------- 1 user user 868 Jun 28 13:55 masterseed.json - -$HOME/.clef/02f90c0603f4f2f60188: -total 12 -drwx------ 2 user user 4096 Jul 1 13:25 . -drwxr-x--x 3 user user 4096 Jul 1 13:45 .. --rw------- 1 user user 159 Jul 1 13:25 config.json - -$ cat ~/.clef/02f90c0603f4f2f60188/config.json -{"ruleset_sha256":{"iv":"SWWEtnl+R+I+wfG7","c":"I3fjmwmamxVcfGax7D0MdUOL29/rBWcs73WBILmYK0o1CrX7wSMc3y37KsmtlZUAjp0oItYq01Ow8VGUOzilG91tDHInB5YHNtm/YkufEbo="}} -``` - -In `$HOME/.clef`, the `masterseed.json` file was created, containing the master seed. This seed was then used to derive a few other things: - -- **Vault location**: in this case `02f90c0603f4f2f60188`. - - 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). -- **`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. - -## Advanced rules - -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`. - -```text -$ clef setpw 0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3 - -Please enter a password to store for this address: -Password: -Repeat password: - -Decrypt master seed of clef -Password: -INFO [07-01|14:05:56.031] Credential store updated key=0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3 -``` - -Now let's update the rules to make use of the new credentials: - -```js -function ApproveListing() { - return "Approve" -} - -function ApproveSignData(req) { - if (req.address.toLowerCase() == "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3") { - if (req.messages[0].value.indexOf("bazonk") >= 0) { - return "Approve" - } - return "Reject" - } - // Otherwise goes to manual processing -} -``` - -In this example: - -- Any requests to sign data with the account `0xd9c9...` will be: - - Auto-approved if the message contains `bazonk`, - - Auto-rejected if the message does not contain `bazonk`, -- Any other requests will be passed along for manual confirmation. - -*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.* - -Attest the new rule file so that Clef will accept loading it: - -```text -$ sha256sum rules.js -f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 rules.js - -$ clef attest f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 -Decrypt master seed of clef -Password: -INFO [07-01|14:11:28.509] Ruleset attestation updated sha256=f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 -``` - -Restart Clef with the new rules in place: - -``` -$ clef --keystore ~/.ethereum/rinkeby/keystore --chainid 4 --rules rules.js - -INFO [07-01|14:12:41.636] Rule engine configured file=rules.js -INFO [07-01|14:12:41.636] Starting signer chainid=4 keystore=$HOME/.ethereum/rinkeby/keystore light-kdf=false advanced=false -DEBUG[07-01|14:12:41.636] FS scan times list=46.722µs set=4.47µs diff=2.157µs -DEBUG[07-01|14:12:41.637] Ledger support enabled -DEBUG[07-01|14:12:41.637] Trezor support enabled via HID -DEBUG[07-01|14:12:41.638] Trezor support enabled via WebUSB -INFO [07-01|14:12:41.638] Audit logs configured file=audit.log -DEBUG[07-01|14:12:41.638] IPC registered namespace=account -INFO [07-01|14:12:41.638] IPC endpoint opened url=$HOME/.clef/clef.ipc -------- Signer info ------- -* intapi_version : 7.0.0 -* extapi_version : 6.0.0 -* extapi_http : n/a -* extapi_ipc : $HOME/.clef/clef.ipc -``` - -Then test signing, once with `bazonk` and once without: - -``` -$ echo '{"id": 1, "jsonrpc":"2.0", "method":"account_signData", "params":["data/plain", "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3", "0x202062617a6f6e6b2062617a2067617a0a"]}' | nc -U ~/.clef/clef.ipc -{"jsonrpc":"2.0","id":1,"result":"0x4f93e3457027f6be99b06b3392d0ebc60615ba448bb7544687ef1248dea4f5317f789002df783979c417d969836b6fda3710f5bffb296b4d51c8aaae6e2ac4831c"} - -$ echo '{"id": 1, "jsonrpc":"2.0", "method":"account_signData", "params":["data/plain", "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3", "0x2020626f6e6b2062617a2067617a0a"]}' | nc -U ~/.clef/clef.ipc -{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Request denied"}} -``` - -Meanwhile, in the Clef output log you can see: -```text -INFO [02-21|14:42:41] Op approved -INFO [02-21|14:42:56] Op rejected -``` - -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: - -```text -$ tail -n 4 audit.log -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 -t=2019-07-01T15:52:14+0300 lvl=info msg=SignData api=signer type=response data=4f93e3457027f6be99b06b3392d0ebc60615ba448bb7544687ef1248dea4f5317f789002df783979c417d969836b6fda3710f5bffb296b4d51c8aaae6e2ac4831c error=nil -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 -t=2019-07-01T15:52:23+0300 lvl=info msg=SignData api=signer type=response data= error="Request denied" -``` - -For more details on writing automatic rules, please see the [rules spec](https://github.com/ethereum/go-ethereum/blob/master/cmd/clef/rules.md). - -## Geth integration - -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. - -Until then however, we're trying to pave the way via Geth. Geth v1.9.0 has built in support via `--signer ` for using a local or remote Clef instance as an account backend! - -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). - -```text -$ clef --keystore ~/.ethereum/rinkeby/keystore --chainid 4 --rules rules.js -``` - -In a different window we can start Geth, list our accounts, even list our wallets to see where the accounts originate from: - -```text -$ geth --rinkeby --signer=~/.clef/clef.ipc console - -> eth.accounts -["0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3", "0x086278a6c067775f71d6b2bb1856db6e28c30418"] - -> personal.listWallets -[{ - accounts: [{ - address: "0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3", - url: "extapi://$HOME/.clef/clef.ipc" - }, { - address: "0x086278a6c067775f71d6b2bb1856db6e28c30418", - url: "extapi://$HOME/.clef/clef.ipc" - }], - status: "ok [version=6.0.0]", - url: "extapi://$HOME/.clef/clef.ipc" -}] - -> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[0]}) -``` - -Lastly, when we requested a transaction to be sent, Clef prompted us in the original window to approve it: - -```text ---------- Transaction request------------- -to: 0xD9C9Cd5f6779558b6e0eD4e6Acf6b1947E7fA1F3 -from: 0xD9C9Cd5f6779558b6e0eD4e6Acf6b1947E7fA1F3 [chksum ok] -value: 0 wei -gas: 0x5208 (21000) -gasprice: 1000000000 wei -nonce: 0x2366 (9062) - -Request context: - NA -> NA -> NA - -Additional HTTP header data, provided by the external caller: - User-Agent: - Origin: -------------------------------------------- -Approve? [y/N]: -> y -``` - -:boom: - -*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.* diff --git a/signer/core/api.go b/signer/core/api.go deleted file mode 100644 index c5cc226b79..0000000000 --- a/signer/core/api.go +++ /dev/null @@ -1,668 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "math/big" - "os" - "reflect" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/accounts/scwallet" - "github.com/ethereum/go-ethereum/accounts/usbwallet" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rpc" - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/ethereum/go-ethereum/signer/storage" -) - -const ( - // numberOfAccountsToDerive For hardware wallets, the number of accounts to derive - numberOfAccountsToDerive = 10 - // ExternalAPIVersion -- see extapi_changelog.md - ExternalAPIVersion = "6.1.0" - // InternalAPIVersion -- see intapi_changelog.md - InternalAPIVersion = "7.0.1" -) - -// ExternalAPI defines the external API through which signing requests are made. -type ExternalAPI interface { - // List available accounts - List(ctx context.Context) ([]common.Address, error) - // New request to create a new account - New(ctx context.Context) (common.Address, error) - // SignTransaction request to sign the specified transaction - SignTransaction(ctx context.Context, args apitypes.SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) - // SignData - request to sign the given data (plus prefix) - SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) - // SignTypedData - request to sign the given structured data (plus prefix) - SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data apitypes.TypedData) (hexutil.Bytes, error) - // EcRecover - recover public key from given message and signature - EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) - // Version info about the APIs - Version(ctx context.Context) (string, error) - // SignGnosisSafeTx signs/confirms a gnosis-safe multisig transaction - SignGnosisSafeTx(ctx context.Context, signerAddress common.MixedcaseAddress, gnosisTx GnosisSafeTx, methodSelector *string) (*GnosisSafeTx, error) -} - -// UIClientAPI specifies what method a UI needs to implement to be able to be used as a -// UI for the signer -type UIClientAPI interface { - // ApproveTx prompt the user for confirmation to request to sign Transaction - ApproveTx(request *SignTxRequest) (SignTxResponse, error) - // ApproveSignData prompt the user for confirmation to request to sign data - ApproveSignData(request *SignDataRequest) (SignDataResponse, error) - // ApproveListing prompt the user for confirmation to list accounts - // the list of accounts to list can be modified by the UI - ApproveListing(request *ListRequest) (ListResponse, error) - // ApproveNewAccount prompt the user for confirmation to create new Account, and reveal to caller - ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) - // ShowError displays error message to user - ShowError(message string) - // ShowInfo displays info message to user - ShowInfo(message string) - // OnApprovedTx notifies the UI about a transaction having been successfully signed. - // This method can be used by a UI to keep track of e.g. how much has been sent to a particular recipient. - OnApprovedTx(tx ethapi.SignTransactionResult) - // OnSignerStartup is invoked when the signer boots, and tells the UI info about external API location and version - // information - OnSignerStartup(info StartupInfo) - // OnInputRequired is invoked when clef requires user input, for example master password or - // pin-code for unlocking hardware wallets - OnInputRequired(info UserInputRequest) (UserInputResponse, error) - // RegisterUIServer tells the UI to use the given UIServerAPI for ui->clef communication - RegisterUIServer(api *UIServerAPI) -} - -// Validator defines the methods required to validate a transaction against some -// sanity defaults as well as any underlying 4byte method database. -// -// Use fourbyte.Database as an implementation. It is separated out of this package -// to allow pieces of the signer package to be used without having to load the -// 7MB embedded 4byte dump. -type Validator interface { - // ValidateTransaction does a number of checks on the supplied transaction, and - // returns either a list of warnings, or an error (indicating that the transaction - // should be immediately rejected). - ValidateTransaction(selector *string, tx *apitypes.SendTxArgs) (*apitypes.ValidationMessages, error) -} - -// SignerAPI defines the actual implementation of ExternalAPI -type SignerAPI struct { - chainID *big.Int - am *accounts.Manager - UI UIClientAPI - validator Validator - rejectMode bool - credentials storage.Storage -} - -// Metadata about a request -type Metadata struct { - Remote string `json:"remote"` - Local string `json:"local"` - Scheme string `json:"scheme"` - UserAgent string `json:"User-Agent"` - Origin string `json:"Origin"` -} - -func StartClefAccountManager(ksLocation string, lightKDF bool, scpath string) *accounts.Manager { - var ( - backends []accounts.Backend - n, p = keystore.StandardScryptN, keystore.StandardScryptP - ) - if lightKDF { - n, p = keystore.LightScryptN, keystore.LightScryptP - } - // support password based accounts - if len(ksLocation) > 0 { - backends = append(backends, keystore.NewKeyStore(ksLocation, n, p)) - } - // Start a USB hub for Ledger hardware wallets - if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil { - log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err)) - } else { - backends = append(backends, ledgerhub) - log.Debug("Ledger support enabled") - } - // Start a USB hub for Trezor hardware wallets (HID version) - if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil { - log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err)) - } else { - backends = append(backends, trezorhub) - log.Debug("Trezor support enabled via HID") - } - // Start a USB hub for Trezor hardware wallets (WebUSB version) - if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil { - log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err)) - } else { - backends = append(backends, trezorhub) - log.Debug("Trezor support enabled via WebUSB") - } - - // Start a smart card hub - if len(scpath) > 0 { - // Sanity check that the smartcard path is valid - fi, err := os.Stat(scpath) - if err != nil { - log.Info("Smartcard socket file missing, disabling", "err", err) - } else { - if fi.Mode()&os.ModeType != os.ModeSocket { - log.Error("Invalid smartcard socket file type", "path", scpath, "type", fi.Mode().String()) - } else { - if schub, err := scwallet.NewHub(scpath, scwallet.Scheme, ksLocation); err != nil { - log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err)) - } else { - backends = append(backends, schub) - } - } - } - } - return accounts.NewManager(nil, backends...) -} - -// MetadataFromContext extracts Metadata from a given context.Context -func MetadataFromContext(ctx context.Context) Metadata { - info := rpc.PeerInfoFromContext(ctx) - - m := Metadata{"NA", "NA", "NA", "", ""} // batman - - if info.Transport != "" { - if info.Transport == "http" { - m.Scheme = info.HTTP.Version - } else { - m.Scheme = info.Transport - } - } - if info.RemoteAddr != "" { - m.Remote = info.RemoteAddr - } - if info.HTTP.Host != "" { - m.Local = info.HTTP.Host - } - m.Origin = info.HTTP.Origin - m.UserAgent = info.HTTP.UserAgent - return m -} - -// String implements Stringer interface -func (m Metadata) String() string { - s, err := json.Marshal(m) - if err == nil { - return string(s) - } - return err.Error() -} - -// types for the requests/response types between signer and UI -type ( - // SignTxRequest contains info about a Transaction to sign - SignTxRequest struct { - Transaction apitypes.SendTxArgs `json:"transaction"` - Callinfo []apitypes.ValidationInfo `json:"call_info"` - Meta Metadata `json:"meta"` - } - // SignTxResponse result from SignTxRequest - SignTxResponse struct { - //The UI may make changes to the TX - Transaction apitypes.SendTxArgs `json:"transaction"` - Approved bool `json:"approved"` - } - SignDataRequest struct { - ContentType string `json:"content_type"` - Address common.MixedcaseAddress `json:"address"` - Rawdata []byte `json:"raw_data"` - Messages []*apitypes.NameValueType `json:"messages"` - Callinfo []apitypes.ValidationInfo `json:"call_info"` - Hash hexutil.Bytes `json:"hash"` - Meta Metadata `json:"meta"` - } - SignDataResponse struct { - Approved bool `json:"approved"` - } - NewAccountRequest struct { - Meta Metadata `json:"meta"` - } - NewAccountResponse struct { - Approved bool `json:"approved"` - } - ListRequest struct { - Accounts []accounts.Account `json:"accounts"` - Meta Metadata `json:"meta"` - } - ListResponse struct { - Accounts []accounts.Account `json:"accounts"` - } - Message struct { - Text string `json:"text"` - } - StartupInfo struct { - Info map[string]interface{} `json:"info"` - } - UserInputRequest struct { - Title string `json:"title"` - Prompt string `json:"prompt"` - IsPassword bool `json:"isPassword"` - } - UserInputResponse struct { - Text string `json:"text"` - } -) - -var ErrRequestDenied = errors.New("request denied") - -// NewSignerAPI creates a new API that can be used for Account management. -// ksLocation specifies the directory where to store the password protected private -// key that is generated when a new Account is created. -// noUSB disables USB support that is required to support hardware devices such as -// ledger and trezor. -func NewSignerAPI(am *accounts.Manager, chainID int64, ui UIClientAPI, validator Validator, advancedMode bool, credentials storage.Storage) *SignerAPI { - if advancedMode { - log.Info("Clef is in advanced mode: will warn instead of reject") - } - signer := &SignerAPI{big.NewInt(chainID), am, ui, validator, !advancedMode, credentials} - signer.startUSBListener() - return signer -} -func (api *SignerAPI) openTrezor(url accounts.URL) { - resp, err := api.UI.OnInputRequired(UserInputRequest{ - Prompt: "Pin required to open Trezor wallet\n" + - "Look at the device for number positions\n\n" + - "7 | 8 | 9\n" + - "--+---+--\n" + - "4 | 5 | 6\n" + - "--+---+--\n" + - "1 | 2 | 3\n\n", - IsPassword: true, - Title: "Trezor unlock", - }) - if err != nil { - log.Warn("failed getting trezor pin", "err", err) - return - } - // We're using the URL instead of the pointer to the - // Wallet -- perhaps it is not actually present anymore - w, err := api.am.Wallet(url.String()) - if err != nil { - log.Warn("wallet unavailable", "url", url) - return - } - err = w.Open(resp.Text) - if err != nil { - log.Warn("failed to open wallet", "wallet", url, "err", err) - return - } -} - -// startUSBListener starts a listener for USB events, for hardware wallet interaction -func (api *SignerAPI) startUSBListener() { - eventCh := make(chan accounts.WalletEvent, 16) - am := api.am - am.Subscribe(eventCh) - // Open any wallets already attached - for _, wallet := range am.Wallets() { - if err := wallet.Open(""); err != nil { - log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err) - if err == usbwallet.ErrTrezorPINNeeded { - go api.openTrezor(wallet.URL()) - } - } - } - go api.derivationLoop(eventCh) -} - -// derivationLoop listens for wallet events -func (api *SignerAPI) derivationLoop(events chan accounts.WalletEvent) { - // Listen for wallet event till termination - for event := range events { - switch event.Kind { - case accounts.WalletArrived: - if err := event.Wallet.Open(""); err != nil { - log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err) - if err == usbwallet.ErrTrezorPINNeeded { - go api.openTrezor(event.Wallet.URL()) - } - } - case accounts.WalletOpened: - status, _ := event.Wallet.Status() - log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status) - var derive = func(limit int, next func() accounts.DerivationPath) { - // Derive first N accounts, hardcoded for now - for i := 0; i < limit; i++ { - path := next() - if acc, err := event.Wallet.Derive(path, true); err != nil { - log.Warn("Account derivation failed", "error", err) - } else { - log.Info("Derived account", "address", acc.Address, "path", path) - } - } - } - log.Info("Deriving default paths") - derive(numberOfAccountsToDerive, accounts.DefaultIterator(accounts.DefaultBaseDerivationPath)) - if event.Wallet.URL().Scheme == "ledger" { - log.Info("Deriving ledger legacy paths") - derive(numberOfAccountsToDerive, accounts.DefaultIterator(accounts.LegacyLedgerBaseDerivationPath)) - log.Info("Deriving ledger live paths") - // For ledger live, since it's based off the same (DefaultBaseDerivationPath) - // as one we've already used, we need to step it forward one step to avoid - // hitting the same path again - nextFn := accounts.LedgerLiveIterator(accounts.DefaultBaseDerivationPath) - nextFn() - derive(numberOfAccountsToDerive, nextFn) - } - case accounts.WalletDropped: - log.Info("Old wallet dropped", "url", event.Wallet.URL()) - event.Wallet.Close() - } - } -} - -// List returns the set of wallet this signer manages. Each wallet can contain -// multiple accounts. -func (api *SignerAPI) List(ctx context.Context) ([]common.Address, error) { - var accs = make([]accounts.Account, 0) - // accs is initialized as empty list, not nil. We use 'nil' to signal - // rejection, as opposed to an empty list. - for _, wallet := range api.am.Wallets() { - accs = append(accs, wallet.Accounts()...) - } - result, err := api.UI.ApproveListing(&ListRequest{Accounts: accs, Meta: MetadataFromContext(ctx)}) - if err != nil { - return nil, err - } - if result.Accounts == nil { - return nil, ErrRequestDenied - } - addresses := make([]common.Address, 0) - for _, acc := range result.Accounts { - addresses = append(addresses, acc.Address) - } - return addresses, nil -} - -// New creates a new password protected Account. The private key is protected with -// the given password. Users are responsible to backup the private key that is stored -// in the keystore location that was specified when this API was created. -func (api *SignerAPI) New(ctx context.Context) (common.Address, error) { - if be := api.am.Backends(keystore.KeyStoreType); len(be) == 0 { - return common.Address{}, errors.New("password based accounts not supported") - } - if resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}); err != nil { - return common.Address{}, err - } else if !resp.Approved { - return common.Address{}, ErrRequestDenied - } - return api.newAccount() -} - -// newAccount is the internal method to create a new account. It should be used -// _after_ user-approval has been obtained -func (api *SignerAPI) newAccount() (common.Address, error) { - be := api.am.Backends(keystore.KeyStoreType) - if len(be) == 0 { - return common.Address{}, errors.New("password based accounts not supported") - } - // Three retries to get a valid password - for i := 0; i < 3; i++ { - resp, err := api.UI.OnInputRequired(UserInputRequest{ - "New account password", - fmt.Sprintf("Please enter a password for the new account to be created (attempt %d of 3)", i), - true}) - if err != nil { - log.Warn("error obtaining password", "attempt", i, "error", err) - continue - } - if pwErr := ValidatePasswordFormat(resp.Text); pwErr != nil { - api.UI.ShowError(fmt.Sprintf("Account creation attempt #%d failed due to password requirements: %v", i+1, pwErr)) - } else { - // No error - acc, err := be[0].(*keystore.KeyStore).NewAccount(resp.Text) - log.Info("Your new key was generated", "address", acc.Address) - log.Warn("Please backup your key file!", "path", acc.URL.Path) - log.Warn("Please remember your password!") - return acc.Address, err - } - } - // Otherwise fail, with generic error message - return common.Address{}, errors.New("account creation failed") -} - -// logDiff logs the difference between the incoming (original) transaction and the one returned from the signer. -// it also returns 'true' if the transaction was modified, to make it possible to configure the signer not to allow -// UI-modifications to requests -func logDiff(original *SignTxRequest, new *SignTxResponse) bool { - var intPtrModified = func(a, b *hexutil.Big) bool { - aBig := (*big.Int)(a) - bBig := (*big.Int)(b) - if aBig != nil && bBig != nil { - return aBig.Cmp(bBig) != 0 - } - // One or both of them are nil - return a != b - } - - modified := false - if f0, f1 := original.Transaction.From, new.Transaction.From; !reflect.DeepEqual(f0, f1) { - log.Info("Sender-account changed by UI", "was", f0, "is", f1) - modified = true - } - if t0, t1 := original.Transaction.To, new.Transaction.To; !reflect.DeepEqual(t0, t1) { - log.Info("Recipient-account changed by UI", "was", t0, "is", t1) - modified = true - } - if g0, g1 := original.Transaction.Gas, new.Transaction.Gas; g0 != g1 { - modified = true - log.Info("Gas changed by UI", "was", g0, "is", g1) - } - if a, b := original.Transaction.GasPrice, new.Transaction.GasPrice; intPtrModified(a, b) { - log.Info("GasPrice changed by UI", "was", a, "is", b) - modified = true - } - if a, b := original.Transaction.MaxPriorityFeePerGas, new.Transaction.MaxPriorityFeePerGas; intPtrModified(a, b) { - log.Info("maxPriorityFeePerGas changed by UI", "was", a, "is", b) - modified = true - } - if a, b := original.Transaction.MaxFeePerGas, new.Transaction.MaxFeePerGas; intPtrModified(a, b) { - log.Info("maxFeePerGas changed by UI", "was", a, "is", b) - modified = true - } - if v0, v1 := big.Int(original.Transaction.Value), big.Int(new.Transaction.Value); v0.Cmp(&v1) != 0 { - modified = true - log.Info("Value changed by UI", "was", v0, "is", v1) - } - if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 { - d0s := "" - d1s := "" - if d0 != nil { - d0s = hexutil.Encode(*d0) - } - if d1 != nil { - d1s = hexutil.Encode(*d1) - } - if d1s != d0s { - modified = true - log.Info("Data changed by UI", "was", d0s, "is", d1s) - } - } - if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 { - modified = true - log.Info("Nonce changed by UI", "was", n0, "is", n1) - } - return modified -} - -func (api *SignerAPI) lookupPassword(address common.Address) (string, error) { - return api.credentials.Get(address.Hex()) -} - -func (api *SignerAPI) lookupOrQueryPassword(address common.Address, title, prompt string) (string, error) { - // Look up the password and return if available - if pw, err := api.lookupPassword(address); err == nil { - return pw, nil - } - // Password unavailable, request it from the user - pwResp, err := api.UI.OnInputRequired(UserInputRequest{title, prompt, true}) - if err != nil { - log.Warn("error obtaining password", "error", err) - // We'll not forward the error here, in case the error contains info about the response from the UI, - // which could leak the password if it was malformed json or something - return "", errors.New("internal error") - } - return pwResp.Text, nil -} - -// SignTransaction signs the given Transaction and returns it both as json and rlp-encoded form -func (api *SignerAPI) SignTransaction(ctx context.Context, args apitypes.SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) { - var ( - err error - result SignTxResponse - ) - msgs, err := api.validator.ValidateTransaction(methodSelector, &args) - if err != nil { - return nil, err - } - // If we are in 'rejectMode', then reject rather than show the user warnings - if api.rejectMode { - if err := msgs.GetWarnings(); err != nil { - log.Info("Signing aborted due to warnings. In order to continue despite warnings, please use the flag '--advanced'.") - return nil, err - } - } - if args.ChainID != nil { - requestedChainId := (*big.Int)(args.ChainID) - if api.chainID.Cmp(requestedChainId) != 0 { - log.Error("Signing request with wrong chain id", "requested", requestedChainId, "configured", api.chainID) - return nil, fmt.Errorf("requested chainid %d does not match the configuration of the signer", - requestedChainId) - } - } - req := SignTxRequest{ - Transaction: args, - Meta: MetadataFromContext(ctx), - Callinfo: msgs.Messages, - } - // Process approval - result, err = api.UI.ApproveTx(&req) - if err != nil { - return nil, err - } - if !result.Approved { - return nil, ErrRequestDenied - } - // Log changes made by the UI to the signing-request - logDiff(&req, &result) - var ( - acc accounts.Account - wallet accounts.Wallet - ) - acc = accounts.Account{Address: result.Transaction.From.Address()} - wallet, err = api.am.Find(acc) - if err != nil { - return nil, err - } - // Convert fields into a real transaction - unsignedTx, err := result.Transaction.ToTransaction() - if err != nil { - return nil, err - } - // Get the password for the transaction - pw, err := api.lookupOrQueryPassword(acc.Address, "Account password", - fmt.Sprintf("Please enter the password for account %s", acc.Address.String())) - if err != nil { - return nil, err - } - // The one to sign is the one that was returned from the UI - signedTx, err := wallet.SignTxWithPassphrase(acc, pw, unsignedTx, api.chainID) - if err != nil { - api.UI.ShowError(err.Error()) - return nil, err - } - - data, err := signedTx.MarshalBinary() - if err != nil { - return nil, err - } - response := ethapi.SignTransactionResult{Raw: data, Tx: signedTx} - - // Finally, send the signed tx to the UI - api.UI.OnApprovedTx(response) - // ...and to the external caller - return &response, nil -} - -func (api *SignerAPI) SignGnosisSafeTx(ctx context.Context, signerAddress common.MixedcaseAddress, gnosisTx GnosisSafeTx, methodSelector *string) (*GnosisSafeTx, error) { - // Do the usual validations, but on the last-stage transaction - args := gnosisTx.ArgsForValidation() - msgs, err := api.validator.ValidateTransaction(methodSelector, args) - if err != nil { - return nil, err - } - // If we are in 'rejectMode', then reject rather than show the user warnings - if api.rejectMode { - if err := msgs.GetWarnings(); err != nil { - log.Info("Signing aborted due to warnings. In order to continue despite warnings, please use the flag '--advanced'.") - return nil, err - } - } - typedData := gnosisTx.ToTypedData() - // might as well error early. - // we are expected to sign. If our calculated hash does not match what they want, - // The gnosis safetx input contains a 'safeTxHash' which is the expected safeTxHash that - sighash, _, err := apitypes.TypedDataAndHash(typedData) - if err != nil { - return nil, err - } - if !bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) { - // It might be the case that the json is missing chain id. - if gnosisTx.ChainId == nil { - gnosisTx.ChainId = (*math.HexOrDecimal256)(api.chainID) - typedData = gnosisTx.ToTypedData() - sighash, _, _ = apitypes.TypedDataAndHash(typedData) - if !bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) { - return nil, fmt.Errorf("mismatched safeTxHash; have %#x want %#x", sighash, gnosisTx.InputExpHash[:]) - } - } - } - signature, preimage, err := api.signTypedData(ctx, signerAddress, typedData, msgs) - - if err != nil { - return nil, err - } - checkSummedSender, _ := common.NewMixedcaseAddressFromString(signerAddress.Address().Hex()) - - gnosisTx.Signature = signature - gnosisTx.SafeTxHash = common.BytesToHash(preimage) - gnosisTx.Sender = *checkSummedSender // Must be checksummed to be accepted by relay - - return &gnosisTx, nil -} - -// Version returns the external api version. This method does not require user acceptance. Available methods are -// available via enumeration anyway, and this info does not contain user-specific data -func (api *SignerAPI) Version(ctx context.Context) (string, error) { - return ExternalAPIVersion, nil -} diff --git a/signer/core/api_test.go b/signer/core/api_test.go deleted file mode 100644 index 7c5cefdf8c..0000000000 --- a/signer/core/api_test.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core_test - -import ( - "bytes" - "fmt" - "math/big" - "os" - "path/filepath" - "testing" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/signer/core" - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/ethereum/go-ethereum/signer/fourbyte" - "github.com/ethereum/go-ethereum/signer/storage" -) - -// Used for testing -type headlessUi struct { - approveCh chan string // to send approve/deny - inputCh chan string // to send password -} - -func (ui *headlessUi) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) { - input := <-ui.inputCh - return core.UserInputResponse{Text: input}, nil -} - -func (ui *headlessUi) OnSignerStartup(info core.StartupInfo) {} -func (ui *headlessUi) RegisterUIServer(api *core.UIServerAPI) {} -func (ui *headlessUi) OnApprovedTx(tx ethapi.SignTransactionResult) {} - -func (ui *headlessUi) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { - switch <-ui.approveCh { - case "Y": - return core.SignTxResponse{request.Transaction, true}, nil - case "M": // modify - // The headless UI always modifies the transaction - old := big.Int(request.Transaction.Value) - newVal := new(big.Int).Add(&old, big.NewInt(1)) - request.Transaction.Value = hexutil.Big(*newVal) - return core.SignTxResponse{request.Transaction, true}, nil - default: - return core.SignTxResponse{request.Transaction, false}, nil - } -} - -func (ui *headlessUi) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { - approved := (<-ui.approveCh == "Y") - return core.SignDataResponse{approved}, nil -} - -func (ui *headlessUi) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { - approval := <-ui.approveCh - //fmt.Printf("approval %s\n", approval) - switch approval { - case "A": - return core.ListResponse{request.Accounts}, nil - case "1": - l := make([]accounts.Account, 1) - l[0] = request.Accounts[1] - return core.ListResponse{l}, nil - default: - return core.ListResponse{nil}, nil - } -} - -func (ui *headlessUi) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { - if <-ui.approveCh == "Y" { - return core.NewAccountResponse{true}, nil - } - return core.NewAccountResponse{false}, nil -} - -func (ui *headlessUi) ShowError(message string) { - // stdout is used by communication - fmt.Fprintln(os.Stderr, message) -} - -func (ui *headlessUi) ShowInfo(message string) { - // stdout is used by communication - fmt.Fprintln(os.Stderr, message) -} - -func tmpDirName(t *testing.T) string { - d := t.TempDir() - d, err := filepath.EvalSymlinks(d) - if err != nil { - t.Fatal(err) - } - return d -} - -func setup(t *testing.T) (*core.SignerAPI, *headlessUi) { - db, err := fourbyte.New() - if err != nil { - t.Fatal(err.Error()) - } - ui := &headlessUi{make(chan string, 20), make(chan string, 20)} - am := core.StartClefAccountManager(tmpDirName(t), true, "") - api := core.NewSignerAPI(am, 1337, ui, db, true, &storage.NoStorage{}) - return api, ui -} -func createAccount(ui *headlessUi, api *core.SignerAPI, t *testing.T) { - ui.approveCh <- "Y" - ui.inputCh <- "a_long_password" - _, err := api.New(t.Context()) - if err != nil { - t.Fatal(err) - } - // Some time to allow changes to propagate - time.Sleep(250 * time.Millisecond) -} - -func failCreateAccountWithPassword(ui *headlessUi, api *core.SignerAPI, password string, t *testing.T) { - ui.approveCh <- "Y" - // We will be asked three times to provide a suitable password - ui.inputCh <- password - ui.inputCh <- password - ui.inputCh <- password - - addr, err := api.New(t.Context()) - if err == nil { - t.Fatal("Should have returned an error") - } - if addr != (common.Address{}) { - t.Fatal("Empty address should be returned") - } -} - -func failCreateAccount(ui *headlessUi, api *core.SignerAPI, t *testing.T) { - ui.approveCh <- "N" - addr, err := api.New(t.Context()) - if err != core.ErrRequestDenied { - t.Fatal(err) - } - if addr != (common.Address{}) { - t.Fatal("Empty address should be returned") - } -} - -func list(ui *headlessUi, api *core.SignerAPI, t *testing.T) ([]common.Address, error) { - ui.approveCh <- "A" - return api.List(t.Context()) -} - -func TestNewAcc(t *testing.T) { - t.Parallel() - api, control := setup(t) - verifyNum := func(num int) { - list, err := list(control, api, t) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - if len(list) != num { - t.Errorf("Expected %d accounts, got %d", num, len(list)) - } - } - // Testing create and create-deny - createAccount(control, api, t) - createAccount(control, api, t) - failCreateAccount(control, api, t) - failCreateAccount(control, api, t) - createAccount(control, api, t) - failCreateAccount(control, api, t) - createAccount(control, api, t) - failCreateAccount(control, api, t) - verifyNum(4) - - // Fail to create this, due to bad password - failCreateAccountWithPassword(control, api, "short", t) - failCreateAccountWithPassword(control, api, "longerbutbad\rfoo", t) - verifyNum(4) - - // Testing listing: - // Listing one Account - control.approveCh <- "1" - list, err := api.List(t.Context()) - if err != nil { - t.Fatal(err) - } - if len(list) != 1 { - t.Fatalf("List should only show one Account") - } - // Listing denied - control.approveCh <- "Nope" - list, err = api.List(t.Context()) - if len(list) != 0 { - t.Fatalf("List should be empty") - } - if err != core.ErrRequestDenied { - t.Fatal("Expected deny") - } -} - -func mkTestTx(from common.MixedcaseAddress) apitypes.SendTxArgs { - to := common.NewMixedcaseAddress(common.HexToAddress("0x1337")) - gas := hexutil.Uint64(21000) - gasPrice := (hexutil.Big)(*big.NewInt(2000000000)) - value := (hexutil.Big)(*big.NewInt(1e18)) - nonce := (hexutil.Uint64)(0) - data := hexutil.Bytes(common.Hex2Bytes("01020304050607080a")) - tx := apitypes.SendTxArgs{ - From: from, - To: &to, - Gas: gas, - GasPrice: &gasPrice, - Value: value, - Data: &data, - Nonce: nonce} - return tx -} - -func TestSignTx(t *testing.T) { - t.Parallel() - var ( - list []common.Address - res, res2 *ethapi.SignTransactionResult - err error - ) - - api, control := setup(t) - createAccount(control, api, t) - control.approveCh <- "A" - list, err = api.List(t.Context()) - if err != nil { - t.Fatal(err) - } - if len(list) == 0 { - t.Fatal("Unexpected empty list") - } - a := common.NewMixedcaseAddress(list[0]) - - methodSig := "test(uint)" - tx := mkTestTx(a) - - control.approveCh <- "Y" - control.inputCh <- "wrongpassword" - res, err = api.SignTransaction(t.Context(), tx, &methodSig) - if res != nil { - t.Errorf("Expected nil-response, got %v", res) - } - if err != keystore.ErrDecrypt { - t.Errorf("Expected ErrDecrypt! %v", err) - } - control.approveCh <- "No way" - res, err = api.SignTransaction(t.Context(), tx, &methodSig) - if res != nil { - t.Errorf("Expected nil-response, got %v", res) - } - if err != core.ErrRequestDenied { - t.Errorf("Expected ErrRequestDenied! %v", err) - } - // Sign with correct password - control.approveCh <- "Y" - control.inputCh <- "a_long_password" - res, err = api.SignTransaction(t.Context(), tx, &methodSig) - if err != nil { - t.Fatal(err) - } - parsedTx := &types.Transaction{} - rlp.DecodeBytes(res.Raw, parsedTx) - - // The tx should NOT be modified by the UI - if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 { - t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value()) - } - control.approveCh <- "Y" - control.inputCh <- "a_long_password" - - res2, err = api.SignTransaction(t.Context(), tx, &methodSig) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(res.Raw, res2.Raw) { - t.Error("Expected tx to be unmodified by UI") - } - - // The tx is modified by the UI - control.approveCh <- "M" - control.inputCh <- "a_long_password" - - res2, err = api.SignTransaction(t.Context(), tx, &methodSig) - if err != nil { - t.Fatal(err) - } - parsedTx2 := &types.Transaction{} - rlp.DecodeBytes(res2.Raw, parsedTx2) - - // The tx should be modified by the UI - if parsedTx2.Value().Cmp(tx.Value.ToInt()) == 0 { - t.Errorf("Expected value to be changed, got %v", parsedTx2.Value()) - } - if bytes.Equal(res.Raw, res2.Raw) { - t.Error("Expected tx to be modified by UI") - } -} diff --git a/signer/core/apitypes/types_test.go b/signer/core/apitypes/types_test.go index f010cef207..2d60383b32 100644 --- a/signer/core/apitypes/types_test.go +++ b/signer/core/apitypes/types_test.go @@ -97,16 +97,6 @@ func TestTxArgs(t *testing.T) { t.Errorf("test %d: have %v, want %v", i, have, want) } } - /* - End to end testing: - - $ go run ./cmd/clef --advanced --suppress-bootwarn - - $ go run ./cmd/geth --nodiscover --maxpeers 0 --signer /home/user/.clef/clef.ipc console - - > tx={"from":"0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425","to":"0x1b442286e32ddcaa6e2570ce9ed85f4b4fc87425","gas":"0x124f8","maxFeePerGas":"0x6fc23ac00","maxPriorityFeePerGas":"0x3b9aca00","value":"0x0","nonce":"0x0","input":"0x","accessList":[],"maxFeePerBlobGas":"0x3b9aca00","blobVersionedHashes":["0x010657f37554c781402a22917dee2f75def7ab966d7b770905398eba3c444014"]} - > eth.signTransaction(tx) - */ } func TestBlobTxs(t *testing.T) { diff --git a/signer/core/auditlog.go b/signer/core/auditlog.go deleted file mode 100644 index 78785a3b02..0000000000 --- a/signer/core/auditlog.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "context" - "encoding/json" - "log/slog" - "os" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/signer/core/apitypes" -) - -type AuditLogger struct { - log log.Logger - api ExternalAPI -} - -func (l *AuditLogger) List(ctx context.Context) ([]common.Address, error) { - l.log.Info("List", "type", "request", "metadata", MetadataFromContext(ctx).String()) - res, e := l.api.List(ctx) - l.log.Info("List", "type", "response", "data", res) - - return res, e -} - -func (l *AuditLogger) New(ctx context.Context) (common.Address, error) { - return l.api.New(ctx) -} - -func (l *AuditLogger) SignTransaction(ctx context.Context, args apitypes.SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) { - sel := "" - if methodSelector != nil { - sel = *methodSelector - } - l.log.Info("SignTransaction", "type", "request", "metadata", MetadataFromContext(ctx).String(), - "tx", args.String(), - "methodSelector", sel) - - res, e := l.api.SignTransaction(ctx, args, methodSelector) - if res != nil { - l.log.Info("SignTransaction", "type", "response", "data", common.Bytes2Hex(res.Raw), "error", e) - } else { - l.log.Info("SignTransaction", "type", "response", "data", res, "error", e) - } - return res, e -} - -func (l *AuditLogger) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) { - marshalledData, _ := json.Marshal(data) // can ignore error, marshalling what we just unmarshalled - l.log.Info("SignData", "type", "request", "metadata", MetadataFromContext(ctx).String(), - "addr", addr.String(), "data", marshalledData, "content-type", contentType) - b, e := l.api.SignData(ctx, contentType, addr, data) - l.log.Info("SignData", "type", "response", "data", common.Bytes2Hex(b), "error", e) - return b, e -} - -func (l *AuditLogger) SignGnosisSafeTx(ctx context.Context, addr common.MixedcaseAddress, gnosisTx GnosisSafeTx, methodSelector *string) (*GnosisSafeTx, error) { - sel := "" - if methodSelector != nil { - sel = *methodSelector - } - data, _ := json.Marshal(gnosisTx) // can ignore error, marshalling what we just unmarshalled - l.log.Info("SignGnosisSafeTx", "type", "request", "metadata", MetadataFromContext(ctx).String(), - "addr", addr.String(), "data", string(data), "selector", sel) - res, e := l.api.SignGnosisSafeTx(ctx, addr, gnosisTx, methodSelector) - if res != nil { - data, _ := json.Marshal(res) // can ignore error, marshalling what we just unmarshalled - l.log.Info("SignGnosisSafeTx", "type", "response", "data", string(data), "error", e) - } else { - l.log.Info("SignGnosisSafeTx", "type", "response", "data", res, "error", e) - } - return res, e -} - -func (l *AuditLogger) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data apitypes.TypedData) (hexutil.Bytes, error) { - l.log.Info("SignTypedData", "type", "request", "metadata", MetadataFromContext(ctx).String(), - "addr", addr.String(), "data", data) - b, e := l.api.SignTypedData(ctx, addr, data) - l.log.Info("SignTypedData", "type", "response", "data", common.Bytes2Hex(b), "error", e) - return b, e -} - -func (l *AuditLogger) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) { - l.log.Info("EcRecover", "type", "request", "metadata", MetadataFromContext(ctx).String(), - "data", common.Bytes2Hex(data), "sig", common.Bytes2Hex(sig)) - b, e := l.api.EcRecover(ctx, data, sig) - l.log.Info("EcRecover", "type", "response", "address", b.String(), "error", e) - return b, e -} - -func (l *AuditLogger) Version(ctx context.Context) (string, error) { - l.log.Info("Version", "type", "request", "metadata", MetadataFromContext(ctx).String()) - data, err := l.api.Version(ctx) - l.log.Info("Version", "type", "response", "data", data, "error", err) - return data, err -} - -func NewAuditLogger(path string, api ExternalAPI) (*AuditLogger, error) { - f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) - if err != nil { - return nil, err - } - - handler := slog.NewTextHandler(f, nil) - l := log.NewLogger(handler).With("api", "signer") - l.Info("Configured", "audit log", path) - return &AuditLogger{l, api}, nil -} diff --git a/signer/core/cliui.go b/signer/core/cliui.go deleted file mode 100644 index e04077865d..0000000000 --- a/signer/core/cliui.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "bufio" - "context" - "encoding/json" - "fmt" - "os" - "strings" - "sync" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/console/prompt" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/log" -) - -type CommandlineUI struct { - in *bufio.Reader - mu sync.Mutex - api *UIServerAPI -} - -func NewCommandlineUI() *CommandlineUI { - return &CommandlineUI{in: bufio.NewReader(os.Stdin)} -} - -func (ui *CommandlineUI) RegisterUIServer(api *UIServerAPI) { - ui.api = api -} - -// readString reads a single line from stdin, trimming if from spaces, enforcing -// non-emptyness. -func (ui *CommandlineUI) readString() string { - for { - fmt.Printf("> ") - text, err := ui.in.ReadString('\n') - if err != nil { - log.Crit("Failed to read user input", "err", err) - } - if text = strings.TrimSpace(text); text != "" { - return text - } - } -} - -func (ui *CommandlineUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) { - fmt.Printf("## %s\n\n%s\n", info.Title, info.Prompt) - defer fmt.Println("-----------------------") - if info.IsPassword { - text, err := prompt.Stdin.PromptPassword("> ") - if err != nil { - log.Error("Failed to read password", "error", err) - return UserInputResponse{}, err - } - return UserInputResponse{text}, nil - } - text := ui.readString() - return UserInputResponse{text}, nil -} - -// confirm returns true if user enters 'Yes', otherwise false -func (ui *CommandlineUI) confirm() bool { - fmt.Printf("Approve? [y/N]:\n") - if ui.readString() == "y" { - return true - } - fmt.Println("-----------------------") - return false -} - -// sanitize quotes and truncates 'txt' if longer than 'limit'. If truncated, -// and ellipsis is added after the quoted string -func sanitize(txt string, limit int) string { - if len(txt) > limit { - return fmt.Sprintf("%q...", txt[:limit]) - } - return fmt.Sprintf("%q", txt) -} - -func showMetadata(metadata Metadata) { - fmt.Printf("Request context:\n\t%v -> %v -> %v\n", metadata.Remote, metadata.Scheme, metadata.Local) - fmt.Printf("\nAdditional HTTP header data, provided by the external caller:\n") - fmt.Printf("\tUser-Agent: %v\n\tOrigin: %v\n", sanitize(metadata.UserAgent, 200), sanitize(metadata.Origin, 100)) -} - -// ApproveTx prompt the user for confirmation to request to sign Transaction -func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { - ui.mu.Lock() - defer ui.mu.Unlock() - weival := request.Transaction.Value.ToInt() - fmt.Printf("--------- Transaction request-------------\n") - if to := request.Transaction.To; to != nil { - fmt.Printf("to: %v\n", to.Original()) - if !to.ValidChecksum() { - fmt.Printf("\nWARNING: Invalid checksum on to-address!\n\n") - } - } else { - fmt.Printf("to: \n") - } - fmt.Printf("from: %v\n", request.Transaction.From.String()) - fmt.Printf("value: %v wei\n", weival) - fmt.Printf("gas: %v (%v)\n", request.Transaction.Gas, uint64(request.Transaction.Gas)) - if request.Transaction.MaxFeePerGas != nil { - fmt.Printf("maxFeePerGas: %v wei\n", request.Transaction.MaxFeePerGas.ToInt()) - fmt.Printf("maxPriorityFeePerGas: %v wei\n", request.Transaction.MaxPriorityFeePerGas.ToInt()) - } else { - fmt.Printf("gasprice: %v wei\n", request.Transaction.GasPrice.ToInt()) - } - fmt.Printf("nonce: %v (%v)\n", request.Transaction.Nonce, uint64(request.Transaction.Nonce)) - if chainId := request.Transaction.ChainID; chainId != nil { - fmt.Printf("chainid: %v\n", chainId) - } - if list := request.Transaction.AccessList; list != nil { - fmt.Printf("Accesslist:\n") - for i, el := range *list { - fmt.Printf(" %d. %v\n", i, el.Address) - for j, slot := range el.StorageKeys { - fmt.Printf(" %d. %v\n", j, slot) - } - } - } - if len(request.Transaction.BlobHashes) > 0 { - fmt.Printf("Blob hashes:\n") - for _, bh := range request.Transaction.BlobHashes { - fmt.Printf(" %v\n", bh) - } - } - if request.Transaction.Data != nil { - d := *request.Transaction.Data - if len(d) > 0 { - fmt.Printf("data: %v\n", hexutil.Encode(d)) - } - } - if request.Callinfo != nil { - fmt.Printf("\nTransaction validation:\n") - for _, m := range request.Callinfo { - fmt.Printf(" * %s : %s\n", m.Typ, m.Message) - } - fmt.Println() - } - fmt.Printf("\n") - showMetadata(request.Meta) - fmt.Printf("-------------------------------------------\n") - if !ui.confirm() { - return SignTxResponse{request.Transaction, false}, nil - } - return SignTxResponse{request.Transaction, true}, nil -} - -// ApproveSignData prompt the user for confirmation to request to sign data -func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { - ui.mu.Lock() - defer ui.mu.Unlock() - - fmt.Printf("-------- Sign data request--------------\n") - fmt.Printf("Account: %s\n", request.Address.String()) - if len(request.Callinfo) != 0 { - fmt.Printf("\nValidation messages:\n") - for _, m := range request.Callinfo { - fmt.Printf(" * %s : %s\n", m.Typ, m.Message) - } - fmt.Println() - } - fmt.Printf("messages:\n") - for _, nvt := range request.Messages { - fmt.Printf("\u00a0\u00a0%v\n", strings.TrimSpace(nvt.Pprint(1))) - } - fmt.Printf("raw data: \n\t%q\n", request.Rawdata) - fmt.Printf("data hash: %v\n", request.Hash) - fmt.Printf("-------------------------------------------\n") - showMetadata(request.Meta) - if !ui.confirm() { - return SignDataResponse{false}, nil - } - return SignDataResponse{true}, nil -} - -// ApproveListing prompt the user for confirmation to list accounts -// the list of accounts to list can be modified by the UI -func (ui *CommandlineUI) ApproveListing(request *ListRequest) (ListResponse, error) { - ui.mu.Lock() - defer ui.mu.Unlock() - - fmt.Printf("-------- List Account request--------------\n") - fmt.Printf("A request has been made to list all accounts. \n") - fmt.Printf("You can select which accounts the caller can see\n") - for _, account := range request.Accounts { - fmt.Printf(" [x] %v\n", account.Address.Hex()) - fmt.Printf(" URL: %v\n", account.URL) - } - fmt.Printf("-------------------------------------------\n") - showMetadata(request.Meta) - if !ui.confirm() { - return ListResponse{nil}, nil - } - return ListResponse{request.Accounts}, nil -} - -// ApproveNewAccount prompt the user for confirmation to create new Account, and reveal to caller -func (ui *CommandlineUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { - ui.mu.Lock() - defer ui.mu.Unlock() - - fmt.Printf("-------- New Account request--------------\n\n") - fmt.Printf("A request has been made to create a new account. \n") - fmt.Printf("Approving this operation means that a new account is created,\n") - fmt.Printf("and the address is returned to the external caller\n\n") - showMetadata(request.Meta) - if !ui.confirm() { - return NewAccountResponse{false}, nil - } - return NewAccountResponse{true}, nil -} - -// ShowError displays error message to user -func (ui *CommandlineUI) ShowError(message string) { - fmt.Printf("## Error \n%s\n", message) - fmt.Printf("-------------------------------------------\n") -} - -// ShowInfo displays info message to user -func (ui *CommandlineUI) ShowInfo(message string) { - fmt.Printf("## Info \n%s\n", message) -} - -func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) { - fmt.Printf("Transaction signed:\n ") - if jsn, err := json.MarshalIndent(tx.Tx, " ", " "); err != nil { - fmt.Printf("WARN: marshalling error %v\n", err) - } else { - fmt.Println(string(jsn)) - } -} - -func (ui *CommandlineUI) showAccounts() { - accounts, err := ui.api.ListAccounts(context.Background()) - if err != nil { - log.Error("Error listing accounts", "err", err) - return - } - if len(accounts) == 0 { - fmt.Print("No accounts found\n") - return - } - var msg string - var out = new(strings.Builder) - if limit := 20; len(accounts) > limit { - msg = fmt.Sprintf("\nFirst %d accounts listed (%d more available).\n", limit, len(accounts)-limit) - accounts = accounts[:limit] - } - fmt.Fprint(out, "\n------- Available accounts -------\n") - for i, account := range accounts { - fmt.Fprintf(out, "%d. %s at %s\n", i, account.Address, account.URL) - } - fmt.Print(out.String(), msg) -} - -func (ui *CommandlineUI) OnSignerStartup(info StartupInfo) { - fmt.Print("\n------- Signer info -------\n") - for k, v := range info.Info { - fmt.Printf("* %v : %v\n", k, v) - } - go ui.showAccounts() -} diff --git a/signer/core/gnosis_safe.go b/signer/core/gnosis_safe.go deleted file mode 100644 index 01724e5383..0000000000 --- a/signer/core/gnosis_safe.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2020 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/signer/core/apitypes" -) - -// GnosisSafeTx is a type to parse the safe-tx returned by the relayer, -// it also conforms to the API required by the Gnosis Safe tx relay service. -// See 'SafeMultisigTransaction' on https://safe-transaction.mainnet.gnosis.io/ -type GnosisSafeTx struct { - // These fields are only used on output - Signature hexutil.Bytes `json:"signature"` - SafeTxHash common.Hash `json:"contractTransactionHash"` - Sender common.MixedcaseAddress `json:"sender"` - // These fields are used both on input and output - Safe common.MixedcaseAddress `json:"safe"` - To common.MixedcaseAddress `json:"to"` - Value math.Decimal256 `json:"value"` - GasPrice math.Decimal256 `json:"gasPrice"` - Data *hexutil.Bytes `json:"data"` - Operation uint8 `json:"operation"` - GasToken common.Address `json:"gasToken"` - RefundReceiver common.Address `json:"refundReceiver"` - BaseGas big.Int `json:"baseGas"` - SafeTxGas big.Int `json:"safeTxGas"` - Nonce big.Int `json:"nonce"` - InputExpHash common.Hash `json:"safeTxHash"` - ChainId *math.HexOrDecimal256 `json:"chainId,omitempty"` -} - -// ToTypedData converts the tx to a EIP-712 Typed Data structure for signing -func (tx *GnosisSafeTx) ToTypedData() apitypes.TypedData { - var data hexutil.Bytes - if tx.Data != nil { - data = *tx.Data - } - var domainType = []apitypes.Type{{Name: "verifyingContract", Type: "address"}} - if tx.ChainId != nil { - domainType = append([]apitypes.Type{{Name: "chainId", Type: "uint256"}}, domainType[0]) - } - - gnosisTypedData := apitypes.TypedData{ - Types: apitypes.Types{ - "EIP712Domain": domainType, - "SafeTx": []apitypes.Type{ - {Name: "to", Type: "address"}, - {Name: "value", Type: "uint256"}, - {Name: "data", Type: "bytes"}, - {Name: "operation", Type: "uint8"}, - {Name: "safeTxGas", Type: "uint256"}, - {Name: "baseGas", Type: "uint256"}, - {Name: "gasPrice", Type: "uint256"}, - {Name: "gasToken", Type: "address"}, - {Name: "refundReceiver", Type: "address"}, - {Name: "nonce", Type: "uint256"}, - }, - }, - Domain: apitypes.TypedDataDomain{ - VerifyingContract: tx.Safe.Address().Hex(), - ChainId: tx.ChainId, - }, - PrimaryType: "SafeTx", - Message: apitypes.TypedDataMessage{ - "to": tx.To.Address().Hex(), - "value": tx.Value.String(), - "data": data, - "operation": fmt.Sprintf("%d", tx.Operation), - "safeTxGas": fmt.Sprintf("%#d", &tx.SafeTxGas), - "baseGas": fmt.Sprintf("%#d", &tx.BaseGas), - "gasPrice": tx.GasPrice.String(), - "gasToken": tx.GasToken.Hex(), - "refundReceiver": tx.RefundReceiver.Hex(), - "nonce": fmt.Sprintf("%d", tx.Nonce.Uint64()), - }, - } - return gnosisTypedData -} - -// ArgsForValidation returns a SendTxArgs struct, which can be used for the -// common validations, e.g. look up 4byte destinations -func (tx *GnosisSafeTx) ArgsForValidation() *apitypes.SendTxArgs { - gp := hexutil.Big(tx.GasPrice) - args := &apitypes.SendTxArgs{ - From: tx.Safe, - To: &tx.To, - Gas: hexutil.Uint64(tx.SafeTxGas.Uint64()), - GasPrice: &gp, - Value: hexutil.Big(tx.Value), - Nonce: hexutil.Uint64(tx.Nonce.Uint64()), - Data: tx.Data, - Input: nil, - ChainID: (*hexutil.Big)(tx.ChainId), - } - return args -} diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go deleted file mode 100644 index d8b6ef0674..0000000000 --- a/signer/core/signed_data.go +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "mime" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/consensus/clique" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/signer/core/apitypes" -) - -// sign receives a request and produces a signature -// -// Note, the produced signature conforms to the secp256k1 curve R, S and V values, -// where the V value will be 27 or 28 for legacy reasons, if legacyV==true. -func (api *SignerAPI) sign(req *SignDataRequest, legacyV bool) (hexutil.Bytes, error) { - // We make the request prior to looking up if we actually have the account, to prevent - // account-enumeration via the API - res, err := api.UI.ApproveSignData(req) - if err != nil { - return nil, err - } - if !res.Approved { - return nil, ErrRequestDenied - } - // Look up the wallet containing the requested signer - account := accounts.Account{Address: req.Address.Address()} - wallet, err := api.am.Find(account) - if err != nil { - return nil, err - } - pw, err := api.lookupOrQueryPassword(account.Address, - "Password for signing", - fmt.Sprintf("Please enter password for signing data with account %s", account.Address.Hex())) - if err != nil { - return nil, err - } - // Sign the data with the wallet - signature, err := wallet.SignDataWithPassphrase(account, pw, req.ContentType, req.Rawdata) - if err != nil { - return nil, err - } - if legacyV { - signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper - } - return signature, nil -} - -// SignData signs the hash of the provided data, but does so differently -// depending on the content-type specified. -// -// Different types of validation occur. -func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) { - var req, transformV, err = api.determineSignatureFormat(ctx, contentType, addr, data) - if err != nil { - return nil, err - } - signature, err := api.sign(req, transformV) - if err != nil { - api.UI.ShowError(err.Error()) - return nil, err - } - return signature, nil -} - -// determineSignatureFormat determines which signature method should be used based upon the mime type -// In the cases where it matters ensure that the charset is handled. The charset -// resides in the 'params' returned as the second returnvalue from mime.ParseMediaType -// charset, ok := params["charset"] -// As it is now, we accept any charset and just treat it as 'raw'. -// This method returns the mimetype for signing along with the request -func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (*SignDataRequest, bool, error) { - var ( - req *SignDataRequest - useEthereumV = true // Default to use V = 27 or 28, the legacy Ethereum format - ) - mediaType, _, err := mime.ParseMediaType(contentType) - if err != nil { - return nil, useEthereumV, err - } - - switch mediaType { - case apitypes.IntendedValidator.Mime: - // Data with an intended validator - validatorData, err := UnmarshalValidatorData(data) - if err != nil { - return nil, useEthereumV, err - } - sighash, msg := SignTextValidator(validatorData) - messages := []*apitypes.NameValueType{ - { - Name: "This is a request to sign data intended for a particular validator (see EIP 191 version 0)", - Typ: "description", - Value: "", - }, - { - Name: "Intended validator address", - Typ: "address", - Value: validatorData.Address.String(), - }, - { - Name: "Application-specific data", - Typ: "hexdata", - Value: validatorData.Message, - }, - { - Name: "Full message for signing", - Typ: "hexdata", - Value: fmt.Sprintf("%#x", msg), - }, - } - req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash} - case apitypes.ApplicationClique.Mime: - // Clique is the Ethereum PoA standard - cliqueData, err := fromHex(data) - if err != nil { - return nil, useEthereumV, err - } - header := &types.Header{} - if err := rlp.DecodeBytes(cliqueData, header); err != nil { - return nil, useEthereumV, err - } - // Add space in the extradata to put the signature - newExtra := make([]byte, len(header.Extra)+65) - copy(newExtra, header.Extra) - header.Extra = newExtra - - // Get back the rlp data, encoded by us - sighash, cliqueRlp, err := cliqueHeaderHashAndRlp(header) - if err != nil { - return nil, useEthereumV, err - } - messages := []*apitypes.NameValueType{ - { - Name: "Clique header", - Typ: "clique", - Value: fmt.Sprintf("clique header %d [%#x]", header.Number, header.Hash()), - }, - } - // Clique uses V on the form 0 or 1 - useEthereumV = false - req = &SignDataRequest{ContentType: mediaType, Rawdata: cliqueRlp, Messages: messages, Hash: sighash} - case apitypes.DataTyped.Mime: - // EIP-712 conformant typed data - var err error - req, err = typedDataRequest(data) - if err != nil { - return nil, useEthereumV, err - } - default: // also case TextPlain.Mime: - // Calculates an Ethereum ECDSA signature for: - // hash = keccak256("\x19Ethereum Signed Message:\n${message length}${message}") - // We expect input to be a hex-encoded string - textData, err := fromHex(data) - if err != nil { - return nil, useEthereumV, err - } - sighash, msg := accounts.TextAndHash(textData) - messages := []*apitypes.NameValueType{ - { - Name: "message", - Typ: accounts.MimetypeTextPlain, - Value: msg, - }, - } - req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash} - } - req.Address = addr - req.Meta = MetadataFromContext(ctx) - return req, useEthereumV, nil -} - -// SignTextValidator signs the given message which can be further recovered -// with the given validator. -// hash = keccak256("\x19\x00"${address}${data}). -func SignTextValidator(validatorData apitypes.ValidatorData) (hexutil.Bytes, string) { - msg := fmt.Sprintf("\x19\x00%s%s", string(validatorData.Address.Bytes()), string(validatorData.Message)) - return crypto.Keccak256([]byte(msg)), msg -} - -// cliqueHeaderHashAndRlp returns the hash which is used as input for the proof-of-authority -// signing. It is the hash of the entire header apart from the 65 byte signature -// contained at the end of the extra data. -// -// The method requires the extra data to be at least 65 bytes -- the original implementation -// in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic -// and simply return an error instead -func cliqueHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) { - if len(header.Extra) < 65 { - err = fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra)) - return - } - rlp = clique.CliqueRLP(header) - hash = clique.SealHash(header).Bytes() - return hash, rlp, err -} - -// SignTypedData signs EIP-712 conformant typed data -// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}") -// It returns -// - the signature, -// - and/or any error -func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData apitypes.TypedData) (hexutil.Bytes, error) { - signature, _, err := api.signTypedData(ctx, addr, typedData, nil) - return signature, err -} - -// signTypedData is identical to the capitalized version, except that it also returns the hash (preimage) -// - the signature preimage (hash) -func (api *SignerAPI) signTypedData(ctx context.Context, addr common.MixedcaseAddress, - typedData apitypes.TypedData, validationMessages *apitypes.ValidationMessages) (hexutil.Bytes, hexutil.Bytes, error) { - req, err := typedDataRequest(typedData) - if err != nil { - return nil, nil, err - } - req.Address = addr - req.Meta = MetadataFromContext(ctx) - if validationMessages != nil { - req.Callinfo = validationMessages.Messages - } - signature, err := api.sign(req, true) - if err != nil { - api.UI.ShowError(err.Error()) - return nil, nil, err - } - return signature, req.Hash, nil -} - -// fromHex tries to interpret the data as type string, and convert from -// hexadecimal to []byte -func fromHex(data any) ([]byte, error) { - if stringData, ok := data.(string); ok { - binary, err := hexutil.Decode(stringData) - return binary, err - } - return nil, fmt.Errorf("wrong type %T", data) -} - -// typedDataRequest tries to convert the data into a SignDataRequest. -func typedDataRequest(data any) (*SignDataRequest, error) { - var typedData apitypes.TypedData - if td, ok := data.(apitypes.TypedData); ok { - typedData = td - } else { // Hex-encoded data - jsonData, err := fromHex(data) - if err != nil { - return nil, err - } - if err = json.Unmarshal(jsonData, &typedData); err != nil { - return nil, err - } - } - messages, err := typedData.Format() - if err != nil { - return nil, err - } - sighash, rawData, err := apitypes.TypedDataAndHash(typedData) - if err != nil { - return nil, err - } - return &SignDataRequest{ - ContentType: apitypes.DataTyped.Mime, - Rawdata: []byte(rawData), - Messages: messages, - Hash: sighash}, nil -} - -// EcRecover recovers the address associated with the given sig. -// Only compatible with `text/plain` -func (api *SignerAPI) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) { - // Returns the address for the Account that was used to create the signature. - // - // Note, this function is compatible with eth_sign. As such it recovers - // the address of: - // hash = keccak256("\x19Ethereum Signed Message:\n${message length}${message}") - // addr = ecrecover(hash, signature) - // - // Note, the signature must conform to the secp256k1 curve R, S and V values, where - // the V value must be 27 or 28 for legacy reasons. - // - // https://geth.ethereum.org/docs/tools/clef/apis#account-ecrecover - if len(sig) != 65 { - return common.Address{}, errors.New("signature must be 65 bytes long") - } - if sig[64] != 27 && sig[64] != 28 { - return common.Address{}, errors.New("invalid Ethereum signature (V is not 27 or 28)") - } - sig = bytes.Clone(sig) // Avoid mutating the input - sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1 - hash := accounts.TextHash(data) - rpk, err := crypto.SigToPub(hash, sig) - if err != nil { - return common.Address{}, err - } - return crypto.PubkeyToAddress(*rpk), nil -} - -// UnmarshalValidatorData converts the bytes input to typed data -func UnmarshalValidatorData(data interface{}) (apitypes.ValidatorData, error) { - raw, ok := data.(map[string]interface{}) - if !ok { - return apitypes.ValidatorData{}, errors.New("validator input is not a map[string]interface{}") - } - addrBytes, err := fromHex(raw["address"]) - if err != nil { - return apitypes.ValidatorData{}, fmt.Errorf("validator address error: %w", err) - } - if len(addrBytes) == 0 { - return apitypes.ValidatorData{}, errors.New("validator address is undefined") - } - messageBytes, err := fromHex(raw["message"]) - if err != nil { - return apitypes.ValidatorData{}, fmt.Errorf("message error: %w", err) - } - if len(messageBytes) == 0 { - return apitypes.ValidatorData{}, errors.New("message is undefined") - } - return apitypes.ValidatorData{ - Address: common.BytesToAddress(addrBytes), - Message: messageBytes, - }, nil -} diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go deleted file mode 100644 index 8455aaf9c5..0000000000 --- a/signer/core/signed_data_test.go +++ /dev/null @@ -1,1062 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core_test - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "math/big" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/signer/core" - "github.com/ethereum/go-ethereum/signer/core/apitypes" -) - -var typesStandard = apitypes.Types{ - "EIP712Domain": { - { - Name: "name", - Type: "string", - }, - { - Name: "version", - Type: "string", - }, - { - Name: "chainId", - Type: "uint256", - }, - { - Name: "verifyingContract", - Type: "address", - }, - }, - "Person": { - { - Name: "name", - Type: "string", - }, - { - Name: "wallet", - Type: "address", - }, - }, - "Mail": { - { - Name: "from", - Type: "Person", - }, - { - Name: "to", - Type: "Person", - }, - { - Name: "contents", - Type: "string", - }, - }, -} - -var jsonTypedData = ` - { - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "test", - "type": "uint8" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "test": 3, - "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } - } -` - -const primaryType = "Mail" - -var domainStandard = apitypes.TypedDataDomain{ - Name: "Ether Mail", - Version: "1", - ChainId: math.NewHexOrDecimal256(1), - VerifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", - Salt: "", -} - -var messageStandard = map[string]interface{}{ - "from": map[string]interface{}{ - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", - }, - "to": map[string]interface{}{ - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", - }, - "contents": "Hello, Bob!", -} - -var typedData = apitypes.TypedData{ - Types: typesStandard, - PrimaryType: primaryType, - Domain: domainStandard, - Message: messageStandard, -} - -func TestSignData(t *testing.T) { - t.Parallel() - api, control := setup(t) - //Create two accounts - createAccount(control, api, t) - createAccount(control, api, t) - control.approveCh <- "1" - list, err := api.List(context.Background()) - if err != nil { - t.Fatal(err) - } - a := common.NewMixedcaseAddress(list[0]) - - control.approveCh <- "Y" - control.inputCh <- "wrongpassword" - signature, err := api.SignData(context.Background(), apitypes.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) - if signature != nil { - t.Errorf("Expected nil-data, got %x", signature) - } - if err != keystore.ErrDecrypt { - t.Errorf("Expected ErrDecrypt! '%v'", err) - } - control.approveCh <- "No way" - signature, err = api.SignData(context.Background(), apitypes.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) - if signature != nil { - t.Errorf("Expected nil-data, got %x", signature) - } - if err != core.ErrRequestDenied { - t.Errorf("Expected ErrRequestDenied! '%v'", err) - } - // text/plain - control.approveCh <- "Y" - control.inputCh <- "a_long_password" - signature, err = api.SignData(context.Background(), apitypes.TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world"))) - if err != nil { - t.Fatal(err) - } - if signature == nil || len(signature) != 65 { - t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) - } - // data/typed via SignTypeData - control.approveCh <- "Y" - control.inputCh <- "a_long_password" - var want []byte - if signature, err = api.SignTypedData(context.Background(), a, typedData); err != nil { - t.Fatal(err) - } else if signature == nil || len(signature) != 65 { - t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) - } else { - want = signature - } - - // data/typed via SignData / mimetype typed data - control.approveCh <- "Y" - control.inputCh <- "a_long_password" - if typedDataJson, err := json.Marshal(typedData); err != nil { - t.Fatal(err) - } else if signature, err = api.SignData(context.Background(), apitypes.DataTyped.Mime, a, hexutil.Encode(typedDataJson)); err != nil { - t.Fatal(err) - } else if signature == nil || len(signature) != 65 { - t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature)) - } else if have := signature; !bytes.Equal(have, want) { - t.Fatalf("want %x, have %x", want, have) - } -} - -func TestDomainChainId(t *testing.T) { - t.Parallel() - withoutChainID := apitypes.TypedData{ - Types: apitypes.Types{ - "EIP712Domain": []apitypes.Type{ - {Name: "name", Type: "string"}, - }, - }, - Domain: apitypes.TypedDataDomain{ - Name: "test", - }, - } - - if _, ok := withoutChainID.Domain.Map()["chainId"]; ok { - t.Errorf("Expected the chainId key to not be present in the domain map") - } - // should encode successfully - if _, err := withoutChainID.HashStruct("EIP712Domain", withoutChainID.Domain.Map()); err != nil { - t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) - } - withChainID := apitypes.TypedData{ - Types: apitypes.Types{ - "EIP712Domain": []apitypes.Type{ - {Name: "name", Type: "string"}, - {Name: "chainId", Type: "uint256"}, - }, - }, - Domain: apitypes.TypedDataDomain{ - Name: "test", - ChainId: math.NewHexOrDecimal256(1), - }, - } - - if _, ok := withChainID.Domain.Map()["chainId"]; !ok { - t.Errorf("Expected the chainId key be present in the domain map") - } - // should encode successfully - if _, err := withChainID.HashStruct("EIP712Domain", withChainID.Domain.Map()); err != nil { - t.Errorf("Expected the typedData to encode the domain successfully, got %v", err) - } -} - -func TestHashStruct(t *testing.T) { - t.Parallel() - hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - t.Fatal(err) - } - mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) - if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" { - t.Errorf("Expected different hashStruct result (got %s)", mainHash) - } - - hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) - if err != nil { - t.Error(err) - } - domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) - if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" { - t.Errorf("Expected different domain hashStruct result (got %s)", domainHash) - } -} - -func TestEncodeType(t *testing.T) { - t.Parallel() - domainTypeEncoding := string(typedData.EncodeType("EIP712Domain")) - if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" { - t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding) - } - - mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType)) - if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" { - t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding) - } -} - -func TestTypeHash(t *testing.T) { - t.Parallel() - mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType))) - if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" { - t.Errorf("Expected different typeHash result (got %s)", mailTypeHash) - } -} - -func TestEncodeData(t *testing.T) { - t.Parallel() - hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0) - if err != nil { - t.Fatal(err) - } - dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash)) - if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" { - t.Errorf("Expected different encodeData result (got %s)", dataEncoding) - } -} - -func TestFormatter(t *testing.T) { - t.Parallel() - var d apitypes.TypedData - err := json.Unmarshal([]byte(jsonTypedData), &d) - if err != nil { - t.Fatalf("unmarshalling failed '%v'", err) - } - formatted, _ := d.Format() - for _, item := range formatted { - t.Logf("'%v'\n", item.Pprint(0)) - } - - j, _ := json.Marshal(formatted) - t.Logf("'%v'\n", string(j)) -} - -func sign(typedData apitypes.TypedData) ([]byte, []byte, error) { - domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map()) - if err != nil { - return nil, nil, err - } - typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message) - if err != nil { - return nil, nil, err - } - rawData := fmt.Appendf(nil, "\x19\x01%s%s", string(domainSeparator), string(typedDataHash)) - sighash := crypto.Keccak256(rawData) - return typedDataHash, sighash, nil -} - -func TestJsonFiles(t *testing.T) { - t.Parallel() - testfiles, err := os.ReadDir("testdata/") - if err != nil { - t.Fatalf("failed reading files: %v", err) - } - for i, fInfo := range testfiles { - if !strings.HasSuffix(fInfo.Name(), "json") { - continue - } - expectedFailure := strings.HasPrefix(fInfo.Name(), "expfail") - data, err := os.ReadFile(filepath.Join("testdata", fInfo.Name())) - if err != nil { - t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) - continue - } - var typedData apitypes.TypedData - err = json.Unmarshal(data, &typedData) - if err != nil { - t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) - continue - } - _, _, err = sign(typedData) - t.Logf("Error %v\n", err) - if err != nil && !expectedFailure { - t.Errorf("Test %d failed, file %v: %v", i, fInfo.Name(), err) - } - if expectedFailure && err == nil { - t.Errorf("Test %d succeeded (expected failure), file %v: %v", i, fInfo.Name(), err) - } - } -} - -// TestFuzzerFiles tests some files that have been found by fuzzing to cause -// crashes or hangs. -func TestFuzzerFiles(t *testing.T) { - t.Parallel() - corpusdir := filepath.Join("testdata", "fuzzing") - testfiles, err := os.ReadDir(corpusdir) - if err != nil { - t.Fatalf("failed reading files: %v", err) - } - verbose := false - for i, fInfo := range testfiles { - data, err := os.ReadFile(filepath.Join(corpusdir, fInfo.Name())) - if err != nil { - t.Errorf("Failed to read file %v: %v", fInfo.Name(), err) - continue - } - var typedData apitypes.TypedData - err = json.Unmarshal(data, &typedData) - if err != nil { - t.Errorf("Test %d, file %v, json unmarshalling failed: %v", i, fInfo.Name(), err) - continue - } - _, err = typedData.EncodeData("EIP712Domain", typedData.Domain.Map(), 1) - if verbose && err != nil { - t.Logf("%d, EncodeData[1] err: %v\n", i, err) - } - _, err = typedData.EncodeData(typedData.PrimaryType, typedData.Message, 1) - if verbose && err != nil { - t.Logf("%d, EncodeData[2] err: %v\n", i, err) - } - typedData.Format() - } -} - -var gnosisTypedData = ` -{ - "types": { - "EIP712Domain": [ - { "type": "address", "name": "verifyingContract" } - ], - "SafeTx": [ - { "type": "address", "name": "to" }, - { "type": "uint256", "name": "value" }, - { "type": "bytes", "name": "data" }, - { "type": "uint8", "name": "operation" }, - { "type": "uint256", "name": "safeTxGas" }, - { "type": "uint256", "name": "baseGas" }, - { "type": "uint256", "name": "gasPrice" }, - { "type": "address", "name": "gasToken" }, - { "type": "address", "name": "refundReceiver" }, - { "type": "uint256", "name": "nonce" } - ] - }, - "domain": { - "verifyingContract": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3" - }, - "primaryType": "SafeTx", - "message": { - "to": "0x9eE457023bB3De16D51A003a247BaEaD7fce313D", - "value": "20000000000000000", - "data": "0x", - "operation": 0, - "safeTxGas": 27845, - "baseGas": 0, - "gasPrice": "0", - "gasToken": "0x0000000000000000000000000000000000000000", - "refundReceiver": "0x0000000000000000000000000000000000000000", - "nonce": 3 - } -}` - -var gnosisTx = ` -{ - "safe": "0x25a6c4BBd32B2424A9c99aEB0584Ad12045382B3", - "to": "0x9eE457023bB3De16D51A003a247BaEaD7fce313D", - "value": "20000000000000000", - "data": null, - "operation": 0, - "gasToken": "0x0000000000000000000000000000000000000000", - "safeTxGas": 27845, - "baseGas": 0, - "gasPrice": "0", - "refundReceiver": "0x0000000000000000000000000000000000000000", - "nonce": 3, - "executionDate": null, - "submissionDate": "2020-09-15T21:59:23.815748Z", - "modified": "2020-09-15T21:59:23.815748Z", - "blockNumber": null, - "transactionHash": null, - "safeTxHash": "0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f", - "executor": null, - "isExecuted": false, - "isSuccessful": null, - "ethGasPrice": null, - "gasUsed": null, - "fee": null, - "origin": null, - "dataDecoded": null, - "confirmationsRequired": null, - "confirmations": [ - { - "owner": "0xAd2e180019FCa9e55CADe76E4487F126Fd08DA34", - "submissionDate": "2020-09-15T21:59:28.281243Z", - "transactionHash": null, - "confirmationType": "CONFIRMATION", - "signature": "0x5e562065a0cb15d766dac0cd49eb6d196a41183af302c4ecad45f1a81958d7797753f04424a9b0aa1cb0448e4ec8e189540fbcdda7530ef9b9d95dfc2d36cb521b", - "signatureType": "EOA" - } - ], - "signatures": null - } -` - -// TestGnosisTypedData tests the scenario where a user submits a full EIP-712 -// struct without using the gnosis-specific endpoint -func TestGnosisTypedData(t *testing.T) { - t.Parallel() - var td apitypes.TypedData - err := json.Unmarshal([]byte(gnosisTypedData), &td) - if err != nil { - t.Fatalf("unmarshalling failed '%v'", err) - } - _, sighash, err := sign(td) - if err != nil { - t.Fatal(err) - } - expSigHash := common.FromHex("0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f") - if !bytes.Equal(expSigHash, sighash) { - t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) - } -} - -// TestGnosisCustomData tests the scenario where a user submits only the gnosis-safe -// specific data, and we fill the TypedData struct on our side -func TestGnosisCustomData(t *testing.T) { - t.Parallel() - var tx core.GnosisSafeTx - err := json.Unmarshal([]byte(gnosisTx), &tx) - if err != nil { - t.Fatal(err) - } - var td = tx.ToTypedData() - _, sighash, err := sign(td) - if err != nil { - t.Fatal(err) - } - expSigHash := common.FromHex("0x28bae2bd58d894a1d9b69e5e9fde3570c4b98a6fc5499aefb54fb830137e831f") - if !bytes.Equal(expSigHash, sighash) { - t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) - } -} - -var gnosisTypedDataWithChainId = ` -{ - "types": { - "EIP712Domain": [ - { "type": "uint256", "name": "chainId" }, - { "type": "address", "name": "verifyingContract" } - ], - "SafeTx": [ - { "type": "address", "name": "to" }, - { "type": "uint256", "name": "value" }, - { "type": "bytes", "name": "data" }, - { "type": "uint8", "name": "operation" }, - { "type": "uint256", "name": "safeTxGas" }, - { "type": "uint256", "name": "baseGas" }, - { "type": "uint256", "name": "gasPrice" }, - { "type": "address", "name": "gasToken" }, - { "type": "address", "name": "refundReceiver" }, - { "type": "uint256", "name": "nonce" } - ] - }, - "domain": { - "verifyingContract": "0x111dAE35D176A9607053e0c46e91F36AFbC1dc57", - "chainId": "4" - }, - "primaryType": "SafeTx", - "message": { - "to": "0x5592EC0cfb4dbc12D3aB100b257153436a1f0FEa", - "value": "0", - "data": "0xa9059cbb00000000000000000000000099d580d3a7fe7bd183b2464517b2cd7ce5a8f15a0000000000000000000000000000000000000000000000000de0b6b3a7640000", - "operation": 0, - "safeTxGas": 0, - "baseGas": 0, - "gasPrice": "0", - "gasToken": "0x0000000000000000000000000000000000000000", - "refundReceiver": "0x0000000000000000000000000000000000000000", - "nonce": 15 - } -}` - -var gnosisTxWithChainId = ` -{ - "safe": "0x111dAE35D176A9607053e0c46e91F36AFbC1dc57", - "to": "0x5592EC0cfb4dbc12D3aB100b257153436a1f0FEa", - "value": "0", - "data": "0xa9059cbb00000000000000000000000099d580d3a7fe7bd183b2464517b2cd7ce5a8f15a0000000000000000000000000000000000000000000000000de0b6b3a7640000", - "operation": 0, - "gasToken": "0x0000000000000000000000000000000000000000", - "safeTxGas": 0, - "baseGas": 0, - "gasPrice": "0", - "refundReceiver": "0x0000000000000000000000000000000000000000", - "nonce": 15, - "executionDate": "2022-01-10T20:00:12Z", - "submissionDate": "2022-01-10T19:59:59.689989Z", - "modified": "2022-01-10T20:00:31.903635Z", - "blockNumber": 9968802, - "transactionHash": "0xc9fef30499ee8984974ab9dddd9d15c2a97c1a4393935dceed5efc3af9fc41a4", - "safeTxHash": "0x6619dab5401503f2735256e12b898e69eb701d6a7e0d07abf1be4bb8aebfba29", - "executor": "0xbc2BB26a6d821e69A38016f3858561a1D80d4182", - "isExecuted": true, - "isSuccessful": true, - "ethGasPrice": "2500000009", - "gasUsed": 82902, - "fee": "207255000746118", - "chainId": "4", - "origin": null, - "dataDecoded": { - "method": "transfer", - "parameters": [ - { - "name": "to", - "type": "address", - "value": "0x99D580d3a7FE7BD183b2464517B2cD7ce5A8F15A" - }, - { - "name": "value", - "type": "uint256", - "value": "1000000000000000000" - } - ] - }, - "confirmationsRequired": 1, - "confirmations": [ - { - "owner": "0xbc2BB26a6d821e69A38016f3858561a1D80d4182", - "submissionDate": "2022-01-10T19:59:59.722500Z", - "transactionHash": null, - "signature": "0x5ca34641bcdee06e7b99143bfe34778195ca41022bd35837b96c204c7786be9d6dfa6dba43b53cd92da45ac728899e1561b232d28f38ba82df45f164caba38be1b", - "signatureType": "EOA" - } - ], - "signatures": "0x5ca34641bcdee06e7b99143bfe34778195ca41022bd35837b96c204c7786be9d6dfa6dba43b53cd92da45ac728899e1561b232d28f38ba82df45f164caba38be1b" -} -` - -func TestGnosisTypedDataWithChainId(t *testing.T) { - t.Parallel() - var td apitypes.TypedData - err := json.Unmarshal([]byte(gnosisTypedDataWithChainId), &td) - if err != nil { - t.Fatalf("unmarshalling failed '%v'", err) - } - _, sighash, err := sign(td) - if err != nil { - t.Fatal(err) - } - expSigHash := common.FromHex("0x6619dab5401503f2735256e12b898e69eb701d6a7e0d07abf1be4bb8aebfba29") - if !bytes.Equal(expSigHash, sighash) { - t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) - } -} - -// TestGnosisCustomDataWithChainId tests the scenario where a user submits only the gnosis-safe -// specific data, and we fill the TypedData struct on our side -func TestGnosisCustomDataWithChainId(t *testing.T) { - t.Parallel() - var tx core.GnosisSafeTx - err := json.Unmarshal([]byte(gnosisTxWithChainId), &tx) - if err != nil { - t.Fatal(err) - } - var td = tx.ToTypedData() - _, sighash, err := sign(td) - if err != nil { - t.Fatal(err) - } - expSigHash := common.FromHex("0x6619dab5401503f2735256e12b898e69eb701d6a7e0d07abf1be4bb8aebfba29") - if !bytes.Equal(expSigHash, sighash) { - t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) - } -} - -var complexTypedData = ` -{ - "types": { - "EIP712Domain": [ - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "name", - "type": "string" - }, - { - "name": "verifyingContract", - "type": "address" - }, - { - "name": "version", - "type": "string" - } - ], - "Action": [ - { - "name": "action", - "type": "string" - }, - { - "name": "params", - "type": "string" - } - ], - "Cell": [ - { - "name": "capacity", - "type": "string" - }, - { - "name": "lock", - "type": "string" - }, - { - "name": "type", - "type": "string" - }, - { - "name": "data", - "type": "string" - }, - { - "name": "extraData", - "type": "string" - } - ], - "Transaction": [ - { - "name": "DAS_MESSAGE", - "type": "string" - }, - { - "name": "inputsCapacity", - "type": "string" - }, - { - "name": "outputsCapacity", - "type": "string" - }, - { - "name": "fee", - "type": "string" - }, - { - "name": "action", - "type": "Action" - }, - { - "name": "inputs", - "type": "Cell[]" - }, - { - "name": "outputs", - "type": "Cell[]" - }, - { - "name": "digest", - "type": "bytes32" - } - ] - }, - "primaryType": "Transaction", - "domain": { - "chainId": "56", - "name": "da.systems", - "verifyingContract": "0x0000000000000000000000000000000020210722", - "version": "1" - }, - "message": { - "DAS_MESSAGE": "SELL mobcion.bit FOR 100000 CKB", - "inputsCapacity": "1216.9999 CKB", - "outputsCapacity": "1216.9998 CKB", - "fee": "0.0001 CKB", - "digest": "0x53a6c0f19ec281604607f5d6817e442082ad1882bef0df64d84d3810dae561eb", - "action": { - "action": "start_account_sale", - "params": "0x00" - }, - "inputs": [ - { - "capacity": "218 CKB", - "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", - "type": "account-cell-type,0x01,0x", - "data": "{ account: mobcion.bit, expired_at: 1670913958 }", - "extraData": "{ status: 0, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" - } - ], - "outputs": [ - { - "capacity": "218 CKB", - "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", - "type": "account-cell-type,0x01,0x", - "data": "{ account: mobcion.bit, expired_at: 1670913958 }", - "extraData": "{ status: 1, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" - }, - { - "capacity": "201 CKB", - "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", - "type": "account-sale-cell-type,0x01,0x", - "data": "0x1209460ef3cb5f1c68ed2c43a3e020eec2d9de6e...", - "extraData": "" - } - ] - } -} -` - -func TestComplexTypedData(t *testing.T) { - t.Parallel() - var td apitypes.TypedData - err := json.Unmarshal([]byte(complexTypedData), &td) - if err != nil { - t.Fatalf("unmarshalling failed '%v'", err) - } - _, sighash, err := sign(td) - if err != nil { - t.Fatal(err) - } - expSigHash := common.FromHex("0x42b1aca82bb6900ff75e90a136de550a58f1a220a071704088eabd5e6ce20446") - if !bytes.Equal(expSigHash, sighash) { - t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) - } -} - -func TestGnosisSafe(t *testing.T) { - t.Parallel() - // json missing chain id - js := "{\n \"safe\": \"0x899FcB1437DE65DC6315f5a69C017dd3F2837557\",\n \"to\": \"0x899FcB1437DE65DC6315f5a69C017dd3F2837557\",\n \"value\": \"0\",\n \"data\": \"0x0d582f13000000000000000000000000d3ed2b8756b942c98c851722f3bd507a17b4745f0000000000000000000000000000000000000000000000000000000000000005\",\n \"operation\": 0,\n \"gasToken\": \"0x0000000000000000000000000000000000000000\",\n \"safeTxGas\": 0,\n \"baseGas\": 0,\n \"gasPrice\": \"0\",\n \"refundReceiver\": \"0x0000000000000000000000000000000000000000\",\n \"nonce\": 0,\n \"executionDate\": null,\n \"submissionDate\": \"2022-02-23T14:09:00.018475Z\",\n \"modified\": \"2022-12-01T15:52:21.214357Z\",\n \"blockNumber\": null,\n \"transactionHash\": null,\n \"safeTxHash\": \"0x6f0f5cffee69087c9d2471e477a63cab2ae171cf433e754315d558d8836274f4\",\n \"executor\": null,\n \"isExecuted\": false,\n \"isSuccessful\": null,\n \"ethGasPrice\": null,\n \"maxFeePerGas\": null,\n \"maxPriorityFeePerGas\": null,\n \"gasUsed\": null,\n \"fee\": null,\n \"origin\": \"https://gnosis-safe.io\",\n \"dataDecoded\": {\n \"method\": \"addOwnerWithThreshold\",\n \"parameters\": [\n {\n \"name\": \"owner\",\n \"type\": \"address\",\n \"value\": \"0xD3Ed2b8756b942c98c851722F3bd507a17B4745F\"\n },\n {\n \"name\": \"_threshold\",\n \"type\": \"uint256\",\n \"value\": \"5\"\n }\n ]\n },\n \"confirmationsRequired\": 4,\n \"confirmations\": [\n {\n \"owner\": \"0x30B714E065B879F5c042A75Bb40a220A0BE27966\",\n \"submissionDate\": \"2022-03-01T14:56:22Z\",\n \"transactionHash\": \"0x6d0a9c83ac7578ef3be1f2afce089fb83b619583dfa779b82f4422fd64ff3ee9\",\n \"signature\": \"0x00000000000000000000000030b714e065b879f5c042a75bb40a220a0be27966000000000000000000000000000000000000000000000000000000000000000001\",\n \"signatureType\": \"APPROVED_HASH\"\n },\n {\n \"owner\": \"0x8300dFEa25Da0eb744fC0D98c23283F86AB8c10C\",\n \"submissionDate\": \"2022-12-01T15:52:21.214357Z\",\n \"transactionHash\": null,\n \"signature\": \"0xbce73de4cc6ee208e933a93c794dcb8ba1810f9848d1eec416b7be4dae9854c07dbf1720e60bbd310d2159197a380c941cfdb55b3ce58f9dd69efd395d7bef881b\",\n \"signatureType\": \"EOA\"\n }\n ],\n \"trusted\": true,\n \"signatures\": null\n}\n" - var gnosisTx core.GnosisSafeTx - if err := json.Unmarshal([]byte(js), &gnosisTx); err != nil { - t.Fatal(err) - } - sighash, _, err := apitypes.TypedDataAndHash(gnosisTx.ToTypedData()) - if err != nil { - t.Fatal(err) - } - if bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) { - t.Fatal("expected inequality") - } - gnosisTx.ChainId = (*math.HexOrDecimal256)(big.NewInt(1)) - sighash, _, _ = apitypes.TypedDataAndHash(gnosisTx.ToTypedData()) - if !bytes.Equal(sighash, gnosisTx.InputExpHash.Bytes()) { - t.Fatal("expected equality") - } -} - -var complexTypedDataLCRefType = ` -{ - "types": { - "EIP712Domain": [ - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "name", - "type": "string" - }, - { - "name": "verifyingContract", - "type": "address" - }, - { - "name": "version", - "type": "string" - } - ], - "Action": [ - { - "name": "action", - "type": "string" - }, - { - "name": "params", - "type": "string" - } - ], - "cCell": [ - { - "name": "capacity", - "type": "string" - }, - { - "name": "lock", - "type": "string" - }, - { - "name": "type", - "type": "string" - }, - { - "name": "data", - "type": "string" - }, - { - "name": "extraData", - "type": "string" - } - ], - "Transaction": [ - { - "name": "DAS_MESSAGE", - "type": "string" - }, - { - "name": "inputsCapacity", - "type": "string" - }, - { - "name": "outputsCapacity", - "type": "string" - }, - { - "name": "fee", - "type": "string" - }, - { - "name": "action", - "type": "Action" - }, - { - "name": "inputs", - "type": "cCell[]" - }, - { - "name": "outputs", - "type": "cCell[]" - }, - { - "name": "digest", - "type": "bytes32" - } - ] - }, - "primaryType": "Transaction", - "domain": { - "chainId": "56", - "name": "da.systems", - "verifyingContract": "0x0000000000000000000000000000000020210722", - "version": "1" - }, - "message": { - "DAS_MESSAGE": "SELL mobcion.bit FOR 100000 CKB", - "inputsCapacity": "1216.9999 CKB", - "outputsCapacity": "1216.9998 CKB", - "fee": "0.0001 CKB", - "digest": "0x53a6c0f19ec281604607f5d6817e442082ad1882bef0df64d84d3810dae561eb", - "action": { - "action": "start_account_sale", - "params": "0x00" - }, - "inputs": [ - { - "capacity": "218 CKB", - "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", - "type": "account-cell-type,0x01,0x", - "data": "{ account: mobcion.bit, expired_at: 1670913958 }", - "extraData": "{ status: 0, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" - } - ], - "outputs": [ - { - "capacity": "218 CKB", - "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", - "type": "account-cell-type,0x01,0x", - "data": "{ account: mobcion.bit, expired_at: 1670913958 }", - "extraData": "{ status: 1, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" - }, - { - "capacity": "201 CKB", - "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", - "type": "account-sale-cell-type,0x01,0x", - "data": "0x1209460ef3cb5f1c68ed2c43a3e020eec2d9de6e...", - "extraData": "" - } - ] - } -} -` - -func TestComplexTypedDataWithLowercaseReftype(t *testing.T) { - t.Parallel() - var td apitypes.TypedData - err := json.Unmarshal([]byte(complexTypedDataLCRefType), &td) - if err != nil { - t.Fatalf("unmarshalling failed '%v'", err) - } - _, sighash, err := sign(td) - if err != nil { - t.Fatal(err) - } - expSigHash := common.FromHex("0x49191f910874f0148597204d9076af128d4694a7c4b714f1ccff330b87207bff") - if !bytes.Equal(expSigHash, sighash) { - t.Fatalf("Error, got %x, wanted %x", sighash, expSigHash) - } -} - -var recursiveBytesTypesStandard = apitypes.Types{ - "EIP712Domain": { - { - Name: "name", - Type: "string", - }, - { - Name: "version", - Type: "string", - }, - { - Name: "chainId", - Type: "uint256", - }, - { - Name: "verifyingContract", - Type: "address", - }, - }, - "Val": { - { - Name: "field", - Type: "bytes[][]", - }, - }, -} - -var recursiveBytesMessageStandard = map[string]interface{}{ - "field": [][][]byte{{{1}, {2}}, {{3}, {4}}}, -} - -var recursiveBytesTypedData = apitypes.TypedData{ - Types: recursiveBytesTypesStandard, - PrimaryType: "Val", - Domain: domainStandard, - Message: recursiveBytesMessageStandard, -} - -func TestEncodeDataRecursiveBytes(t *testing.T) { - typedData := recursiveBytesTypedData - _, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0) - if err != nil { - t.Fatalf("got err %v", err) - } -} diff --git a/signer/core/stdioui.go b/signer/core/stdioui.go deleted file mode 100644 index a0ce684417..0000000000 --- a/signer/core/stdioui.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "context" - - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rpc" -) - -type StdIOUI struct { - client *rpc.Client -} - -func NewStdIOUI() *StdIOUI { - client, err := rpc.DialContext(context.Background(), "stdio://") - if err != nil { - log.Crit("Could not create stdio client", "err", err) - } - ui := &StdIOUI{client: client} - return ui -} - -func (ui *StdIOUI) RegisterUIServer(api *UIServerAPI) { - ui.client.RegisterName("clef", api) -} - -// dispatch sends a request over the stdio -func (ui *StdIOUI) dispatch(serviceMethod string, args interface{}, reply interface{}) error { - err := ui.client.Call(&reply, serviceMethod, args) - if err != nil { - log.Info("Error", "exc", err.Error()) - } - return err -} - -// notify sends a request over the stdio, and does not listen for a response -func (ui *StdIOUI) notify(serviceMethod string, args interface{}) error { - ctx := context.Background() - err := ui.client.Notify(ctx, serviceMethod, args) - if err != nil { - log.Info("Error", "exc", err.Error()) - } - return err -} - -func (ui *StdIOUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) { - var result SignTxResponse - err := ui.dispatch("ui_approveTx", request, &result) - return result, err -} - -func (ui *StdIOUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) { - var result SignDataResponse - err := ui.dispatch("ui_approveSignData", request, &result) - return result, err -} - -func (ui *StdIOUI) ApproveListing(request *ListRequest) (ListResponse, error) { - var result ListResponse - err := ui.dispatch("ui_approveListing", request, &result) - return result, err -} - -func (ui *StdIOUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) { - var result NewAccountResponse - err := ui.dispatch("ui_approveNewAccount", request, &result) - return result, err -} - -func (ui *StdIOUI) ShowError(message string) { - err := ui.notify("ui_showError", &Message{message}) - if err != nil { - log.Info("Error calling 'ui_showError'", "exc", err.Error(), "msg", message) - } -} - -func (ui *StdIOUI) ShowInfo(message string) { - err := ui.notify("ui_showInfo", Message{message}) - if err != nil { - log.Info("Error calling 'ui_showInfo'", "exc", err.Error(), "msg", message) - } -} -func (ui *StdIOUI) OnApprovedTx(tx ethapi.SignTransactionResult) { - err := ui.notify("ui_onApprovedTx", tx) - if err != nil { - log.Info("Error calling 'ui_onApprovedTx'", "exc", err.Error(), "tx", tx) - } -} - -func (ui *StdIOUI) OnSignerStartup(info StartupInfo) { - err := ui.notify("ui_onSignerStartup", info) - if err != nil { - log.Info("Error calling 'ui_onSignerStartup'", "exc", err.Error(), "info", info) - } -} -func (ui *StdIOUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) { - var result UserInputResponse - err := ui.dispatch("ui_onInputRequired", info, &result) - if err != nil { - log.Info("Error calling 'ui_onInputRequired'", "exc", err.Error(), "info", info) - } - return result, err -} diff --git a/signer/core/testdata/README.md b/signer/core/testdata/README.md deleted file mode 100644 index 85aa70c04c..0000000000 --- a/signer/core/testdata/README.md +++ /dev/null @@ -1,5 +0,0 @@ -### EIP-712 tests - -These tests are json files which are converted into [EIP-712](https://eips.ethereum.org/EIPS/eip-712) typed data. -All files are expected to be proper json, and tests will fail if they are not. -Files that begin with `expfail' are expected to not pass the hashstruct construction. diff --git a/signer/core/testdata/arrays-1.json b/signer/core/testdata/arrays-1.json deleted file mode 100644 index fea82b42c6..0000000000 --- a/signer/core/testdata/arrays-1.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Foo": [ - { - "name": "addys", - "type": "address[]" - }, - { - "name": "stringies", - "type": "string[]" - }, - { - "name": "inties", - "type": "uint[]" - } - ] - }, - "primaryType": "Foo", - "domain": { - "name": "Lorem", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "addys": [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003" - ], - "stringies": [ - "lorem", - "ipsum", - "dolores" - ], - "inties": [ - "0x0000000000000000000000000000000000000001", - "3", - 4.0 - ] - } -} diff --git a/signer/core/testdata/custom_arraytype.json b/signer/core/testdata/custom_arraytype.json deleted file mode 100644 index 078de88c22..0000000000 --- a/signer/core/testdata/custom_arraytype.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person[]" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { "name": "Cow"}, - "to": [{ "name": "Moose"},{ "name": "Goose"}], - "contents": "Hello, Bob!" - } -} diff --git a/signer/core/testdata/eip712.json b/signer/core/testdata/eip712.json deleted file mode 100644 index 7b1cb8ae2d..0000000000 --- a/signer/core/testdata/eip712.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "test", - "type": "uint8" - }, - { - "name": "test2", - "type": "uint8" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "test": "3", - "test2": 5.0, - "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "test": "0", - "test2": 5, - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } -} diff --git a/signer/core/testdata/expfail_arraytype_overload.json b/signer/core/testdata/expfail_arraytype_overload.json deleted file mode 100644 index 786487f100..0000000000 --- a/signer/core/testdata/expfail_arraytype_overload.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Person[]": [ - { - "name": "baz", - "type": "string" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person[]" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": {"baz": "foo"}, - "contents": "Hello, Bob!" - } -} diff --git a/signer/core/testdata/expfail_datamismatch_1.json b/signer/core/testdata/expfail_datamismatch_1.json deleted file mode 100644 index d19d470d1e..0000000000 --- a/signer/core/testdata/expfail_datamismatch_1.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "Person" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } -} diff --git a/signer/core/testdata/expfail_extradata.json b/signer/core/testdata/expfail_extradata.json deleted file mode 100644 index 10f91c23af..0000000000 --- a/signer/core/testdata/expfail_extradata.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "test", - "type": "uint8" - }, - { - "name": "test2", - "type": "uint8" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "blahonga": "zonk bonk", - "from": { - "name": "Cow", - "test": "3", - "test2": 5.0, - "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "test": "0", - "test2": 5, - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } -} diff --git a/signer/core/testdata/expfail_malformeddomainkeys.json b/signer/core/testdata/expfail_malformeddomainkeys.json deleted file mode 100644 index 354b3cc859..0000000000 --- a/signer/core/testdata/expfail_malformeddomainkeys.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "vFAILFAILerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } -} diff --git a/signer/core/testdata/expfail_nonexistant_type.json b/signer/core/testdata/expfail_nonexistant_type.json deleted file mode 100644 index d06bc20b9f..0000000000 --- a/signer/core/testdata/expfail_nonexistant_type.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "Blahonga" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } -} diff --git a/signer/core/testdata/expfail_nonexistant_type2.json b/signer/core/testdata/expfail_nonexistant_type2.json deleted file mode 100644 index fd704209bc..0000000000 --- a/signer/core/testdata/expfail_nonexistant_type2.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256 ... and now for something completely different" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "test", - "type": "uint8" - }, - { - "name": "test2", - "type": "uint8" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "Cow", - "test": "3", - "test2": 5.0, - "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "Bob", - "test": "0", - "test2": 5, - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "contents": "Hello, Bob!" - } -} diff --git a/signer/core/testdata/expfail_toolargeuint.json b/signer/core/testdata/expfail_toolargeuint.json deleted file mode 100644 index 9854b65b17..0000000000 --- a/signer/core/testdata/expfail_toolargeuint.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Mail": [ - { - "name": "test", - "type": "uint8" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "test":"257" - } -} diff --git a/signer/core/testdata/expfail_toolargeuint2.json b/signer/core/testdata/expfail_toolargeuint2.json deleted file mode 100644 index c63ed41f9c..0000000000 --- a/signer/core/testdata/expfail_toolargeuint2.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Mail": [ - { - "name": "test", - "type": "uint8" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "test":257 - } -} diff --git a/signer/core/testdata/expfail_unconvertiblefloat.json b/signer/core/testdata/expfail_unconvertiblefloat.json deleted file mode 100644 index 8229a333ca..0000000000 --- a/signer/core/testdata/expfail_unconvertiblefloat.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Mail": [ - { - "name": "test", - "type": "uint8" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "test":"255.3" - } -} diff --git a/signer/core/testdata/expfail_unconvertiblefloat2.json b/signer/core/testdata/expfail_unconvertiblefloat2.json deleted file mode 100644 index 59e6d38d24..0000000000 --- a/signer/core/testdata/expfail_unconvertiblefloat2.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Mail": [ - { - "name": "test", - "type": "uint8" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "test": 255.3 - } -} diff --git a/signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f b/signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f deleted file mode 100644 index 8229a333ca..0000000000 --- a/signer/core/testdata/fuzzing/2850f6ccf2d7f5f846dfb73119b60e09e712783f +++ /dev/null @@ -1,38 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Mail": [ - { - "name": "test", - "type": "uint8" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "test":"255.3" - } -} diff --git a/signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92 b/signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92 deleted file mode 100644 index fea82b42c6..0000000000 --- a/signer/core/testdata/fuzzing/36fb987a774011dc675e1b5246ac5c1d44d84d92 +++ /dev/null @@ -1,60 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Foo": [ - { - "name": "addys", - "type": "address[]" - }, - { - "name": "stringies", - "type": "string[]" - }, - { - "name": "inties", - "type": "uint[]" - } - ] - }, - "primaryType": "Foo", - "domain": { - "name": "Lorem", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "addys": [ - "0x0000000000000000000000000000000000000001", - "0x0000000000000000000000000000000000000002", - "0x0000000000000000000000000000000000000003" - ], - "stringies": [ - "lorem", - "ipsum", - "dolores" - ], - "inties": [ - "0x0000000000000000000000000000000000000001", - "3", - 4.0 - ] - } -} diff --git a/signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d b/signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d deleted file mode 100644 index c63ed41f9c..0000000000 --- a/signer/core/testdata/fuzzing/37ec7b55c7ba014cced204c5f9989d2d0eb9ff6d +++ /dev/null @@ -1,38 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "uint256" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Mail": [ - { - "name": "test", - "type": "uint8" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "Ether Mail", - "version": "1", - "chainId": "1", - "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "test":257 - } -} diff --git a/signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1 b/signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1 deleted file mode 100644 index 9bc43938d5..0000000000 --- a/signer/core/testdata/fuzzing/582fa92154b784daa1faa293b695fa388fe34bf1 +++ /dev/null @@ -1 +0,0 @@ -{"domain":{"version":"0","chainId":""}} \ No newline at end of file diff --git a/signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8 b/signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8 deleted file mode 100644 index fe27de916c..0000000000 --- a/signer/core/testdata/fuzzing/ab57cb2b2b5ce614efe13a47bc73814580f2cce8 +++ /dev/null @@ -1,54 +0,0 @@ -{ "types": { "":[ { - "name": "name", - "type":"string" }, - { - "name":"version", - "type": "string" }, { - "name": "chaiI", - "type":"uint256 . ad nowretig omeedifere" }, { - "ae": "eifinC", - "ty":"dess" - } - ], - "Person":[ - { - "name":"name", - "type": "string" - }, { - "name":"tes", "type":"it8" - }, - { "name":"t", "tye":"uit8" - }, - { - "a":"ale", - "type": "ress" - } - ], - "Mail": [ - { - "name":"from", "type":"Person" }, - { - "name": "to", "type": "Person" - }, - { - "name": "contents", - "type": "string" - } - ] - }, "primaryType": "Mail", - "domain": { -"name":"theMail", "version": "1", - "chainId": "1", - "verifyingntract": "0xCcccCCCcCCCCCCCcCCcCCCcCcccccC" - }, - "message": { "from": { - "name": "Cow", - "test": "3", - "est2":5.0, - "llt": "0xcD2a3938E13D947E0bE734DfDD86" }, "to": { "name": "Bob", - "ts":"", - "tet2": 5, - "allet": "0bBBBBbbBBbbbbBbbBbbbbBBBbB" - }, - "contents": "Hello, Bob!" } -} \ No newline at end of file diff --git a/signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d b/signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d deleted file mode 100644 index c5e14b39ed..0000000000 --- a/signer/core/testdata/fuzzing/e4303e23ca34fbbc43164a232b2caa7a3af2bf8d +++ /dev/null @@ -1,64 +0,0 @@ -{ - "types": { - "EIP712Domain": [ - { - "name": "name", - "type": "string" - }, - { - "name": "version", - "type": "string" - }, - { - "name": "chainId", - "type": "int" - }, - { - "name": "verifyingContract", - "type": "address" - } - ], - "Person": [ - { - "name": "name", - "type": "string" - }, - { - "name": "wallet", - "type": "address" - } - ], - "Mail": [ - { - "name": "from", - "type": "Person" - }, - { - "name": "to", - "type": "Mail" - }, - { - "name": "s", - "type": "Person" - } - ] - }, - "primaryType": "Mail", - "domain": { - "name": "l", - "version": "1", - "chainId": "", - "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC" - }, - "message": { - "from": { - "name": "", - "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826" - }, - "to": { - "name": "", - "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB" - }, - "": "" - } -} diff --git a/signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921 b/signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921 deleted file mode 100644 index c4841cb07b..0000000000 --- a/signer/core/testdata/fuzzing/f658340af009dd4a35abe645a00a7b732bc30921 +++ /dev/null @@ -1 +0,0 @@ -{"types":{"0":[{}]}} \ No newline at end of file diff --git a/signer/core/uiapi.go b/signer/core/uiapi.go deleted file mode 100644 index 09ee4b492f..0000000000 --- a/signer/core/uiapi.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2019 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "math/big" - "os" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" -) - -// UIServerAPI implements methods Clef provides for a UI to query, in the bidirectional communication -// channel. -// This API is considered secure, since a request can only -// ever arrive from the UI -- and the UI is capable of approving any action, thus we can consider these -// requests pre-approved. -// NB: It's very important that these methods are not ever exposed on the external service -// registry. -type UIServerAPI struct { - extApi *SignerAPI - am *accounts.Manager -} - -// NewUIServerAPI creates a new UIServerAPI -func NewUIServerAPI(extapi *SignerAPI) *UIServerAPI { - return &UIServerAPI{extapi, extapi.am} -} - -// ListAccounts lists available accounts. As opposed to the external API definition, this method delivers -// the full Account object and not only Address. -// Example call -// {"jsonrpc":"2.0","method":"clef_listAccounts","params":[], "id":4} -func (api *UIServerAPI) ListAccounts(ctx context.Context) ([]accounts.Account, error) { - var accs []accounts.Account - for _, wallet := range api.am.Wallets() { - accs = append(accs, wallet.Accounts()...) - } - return accs, nil -} - -// rawWallet is a JSON representation of an accounts.Wallet interface, with its -// data contents extracted into plain fields. -type rawWallet struct { - URL string `json:"url"` - Status string `json:"status"` - Failure string `json:"failure,omitempty"` - Accounts []accounts.Account `json:"accounts,omitempty"` -} - -// ListWallets will return a list of wallets that clef manages -// Example call -// {"jsonrpc":"2.0","method":"clef_listWallets","params":[], "id":5} -func (api *UIServerAPI) ListWallets() []rawWallet { - allWallets := api.am.Wallets() - wallets := make([]rawWallet, 0, len(allWallets)) // return [] instead of nil if empty - for _, wallet := range allWallets { - status, failure := wallet.Status() - - raw := rawWallet{ - URL: wallet.URL().String(), - Status: status, - Accounts: wallet.Accounts(), - } - if failure != nil { - raw.Failure = failure.Error() - } - wallets = append(wallets, raw) - } - return wallets -} - -// DeriveAccount requests a HD wallet to derive a new account, optionally pinning -// it for later reuse. -// Example call -// {"jsonrpc":"2.0","method":"clef_deriveAccount","params":["ledger://","m/44'/60'/0'", false], "id":6} -func (api *UIServerAPI) DeriveAccount(url string, path string, pin *bool) (accounts.Account, error) { - wallet, err := api.am.Wallet(url) - if err != nil { - return accounts.Account{}, err - } - derivPath, err := accounts.ParseDerivationPath(path) - if err != nil { - return accounts.Account{}, err - } - if pin == nil { - pin = new(bool) - } - return wallet.Derive(derivPath, *pin) -} - -// fetchKeystore retrieves the encrypted keystore from the account manager. -func fetchKeystore(am *accounts.Manager) *keystore.KeyStore { - ks := am.Backends(keystore.KeyStoreType) - if len(ks) == 0 { - return nil - } - return ks[0].(*keystore.KeyStore) -} - -// ImportRawKey stores the given hex encoded ECDSA key into the key directory, -// encrypting it with the passphrase. -// Example call (should fail on password too short) -// {"jsonrpc":"2.0","method":"clef_importRawKey","params":["1111111111111111111111111111111111111111111111111111111111111111","test"], "id":6} -func (api *UIServerAPI) ImportRawKey(privkey string, password string) (accounts.Account, error) { - key, err := crypto.HexToECDSA(privkey) - if err != nil { - return accounts.Account{}, err - } - if err := ValidatePasswordFormat(password); err != nil { - return accounts.Account{}, fmt.Errorf("password requirements not met: %v", err) - } - ks := fetchKeystore(api.am) - if ks == nil { - return accounts.Account{}, errors.New("password based accounts not supported") - } - // No error - return ks.ImportECDSA(key, password) -} - -// OpenWallet initiates a hardware wallet opening procedure, establishing a USB -// connection and attempting to authenticate via the provided passphrase. Note, -// the method may return an extra challenge requiring a second open (e.g. the -// Trezor PIN matrix challenge). -// Example -// {"jsonrpc":"2.0","method":"clef_openWallet","params":["ledger://",""], "id":6} -func (api *UIServerAPI) OpenWallet(url string, passphrase *string) error { - wallet, err := api.am.Wallet(url) - if err != nil { - return err - } - pass := "" - if passphrase != nil { - pass = *passphrase - } - return wallet.Open(pass) -} - -// ChainId returns the chainid in use for Eip-155 replay protection -// Example call -// {"jsonrpc":"2.0","method":"clef_chainId","params":[], "id":8} -func (api *UIServerAPI) ChainId() math.HexOrDecimal64 { - return (math.HexOrDecimal64)(api.extApi.chainID.Uint64()) -} - -// SetChainId sets the chain id to use when signing transactions. -// Example call to set Ropsten: -// {"jsonrpc":"2.0","method":"clef_setChainId","params":["3"], "id":8} -func (api *UIServerAPI) SetChainId(id math.HexOrDecimal64) math.HexOrDecimal64 { - api.extApi.chainID = new(big.Int).SetUint64(uint64(id)) - return api.ChainId() -} - -// Export returns encrypted private key associated with the given address in web3 keystore format. -// Example -// {"jsonrpc":"2.0","method":"clef_export","params":["0x19e7e376e7c213b7e7e7e46cc70a5dd086daff2a"], "id":4} -func (api *UIServerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) { - // Look up the wallet containing the requested signer - wallet, err := api.am.Find(accounts.Account{Address: addr}) - if err != nil { - return nil, err - } - if wallet.URL().Scheme != keystore.KeyStoreScheme { - return nil, errors.New("account is not a keystore-account") - } - return os.ReadFile(wallet.URL().Path) -} - -// Import tries to import the given keyJSON in the local keystore. The keyJSON data is expected to be -// in web3 keystore format. It will decrypt the keyJSON with the given passphrase and on successful -// decryption it will encrypt the key with the given newPassphrase and store it in the keystore. -// Example (the address in question has privkey `11...11`): -// {"jsonrpc":"2.0","method":"clef_import","params":[{"address":"19e7e376e7c213b7e7e7e46cc70a5dd086daff2a","crypto":{"cipher":"aes-128-ctr","ciphertext":"33e4cd3756091d037862bb7295e9552424a391a6e003272180a455ca2a9fb332","cipherparams":{"iv":"b54b263e8f89c42bb219b6279fba5cce"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e4ca94644fd30569c1b1afbbc851729953c92637b7fe4bb9840bbb31ffbc64a5"},"mac":"f4092a445c2b21c0ef34f17c9cd0d873702b2869ec5df4439a0c2505823217e7"},"id":"216c7eac-e8c1-49af-a215-fa0036f29141","version":3},"test","yaddayadda"], "id":4} -func (api *UIServerAPI) Import(ctx context.Context, keyJSON json.RawMessage, oldPassphrase, newPassphrase string) (accounts.Account, error) { - be := api.am.Backends(keystore.KeyStoreType) - - if len(be) == 0 { - return accounts.Account{}, errors.New("password based accounts not supported") - } - if err := ValidatePasswordFormat(newPassphrase); err != nil { - return accounts.Account{}, fmt.Errorf("password requirements not met: %v", err) - } - return be[0].(*keystore.KeyStore).Import(keyJSON, oldPassphrase, newPassphrase) -} - -// New creates a new password protected Account. The private key is protected with -// the given password. Users are responsible to backup the private key that is stored -// in the keystore location that was specified when this API was created. -// This method is the same as New on the external API, the difference being that -// this implementation does not ask for confirmation, since it's initiated by -// the user -func (api *UIServerAPI) New(ctx context.Context) (common.Address, error) { - return api.extApi.newAccount() -} - -// Other methods to be added, not yet implemented are: -// - Ruleset interaction: add rules, attest rulefiles -// - Store metadata about accounts, e.g. naming of accounts diff --git a/signer/core/validation.go b/signer/core/validation.go deleted file mode 100644 index 7639dbf649..0000000000 --- a/signer/core/validation.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "errors" - "regexp" -) - -var printable7BitAscii = regexp.MustCompile("^[A-Za-z0-9!\"#$%&'()*+,\\-./:;<=>?@[\\]^_`{|}~ ]+$") - -// ValidatePasswordFormat returns an error if the password is too short, or consists of characters -// outside the range of the printable 7bit ascii set -func ValidatePasswordFormat(password string) error { - if len(password) < 10 { - return errors.New("password too short (<10 characters)") - } - if !printable7BitAscii.MatchString(password) { - return errors.New("password contains invalid characters - only 7bit printable ascii allowed") - } - return nil -} diff --git a/signer/core/validation_test.go b/signer/core/validation_test.go deleted file mode 100644 index 7f733b0bb1..0000000000 --- a/signer/core/validation_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import "testing" - -func TestPasswordValidation(t *testing.T) { - t.Parallel() - testcases := []struct { - pw string - shouldFail bool - }{ - {"test", true}, - {"testtest\xbd\xb2\x3d\xbc\x20\xe2\x8c\x98", true}, - {"placeOfInterest⌘", true}, - {"password\nwith\nlinebreak", true}, - {"password\twith\vtabs", true}, - // Ok passwords - {"password WhichIsOk", false}, - {"passwordOk!@#$%^&*()", false}, - {"12301203123012301230123012", false}, - } - for _, test := range testcases { - err := ValidatePasswordFormat(test.pw) - if err == nil && test.shouldFail { - t.Errorf("password '%v' should fail validation", test.pw) - } else if err != nil && !test.shouldFail { - t.Errorf("password '%v' shound not fail validation, but did: %v", test.pw, err) - } - } -} diff --git a/signer/rules/rules.go b/signer/rules/rules.go deleted file mode 100644 index c9921e57a9..0000000000 --- a/signer/rules/rules.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rules - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "strings" - - "github.com/dop251/goja" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/internal/jsre/deps" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/signer/core" - "github.com/ethereum/go-ethereum/signer/storage" -) - -// consoleOutput is an override for the console.log and console.error methods to -// stream the output into the configured output stream instead of stdout. -func consoleOutput(call goja.FunctionCall) goja.Value { - output := []string{"JS:> "} - for _, argument := range call.Arguments { - output = append(output, fmt.Sprintf("%v", argument)) - } - fmt.Fprintln(os.Stderr, strings.Join(output, " ")) - return goja.Undefined() -} - -// rulesetUI provides an implementation of UIClientAPI that evaluates a javascript -// file for each defined UI-method -type rulesetUI struct { - next core.UIClientAPI // The next handler, for manual processing - storage storage.Storage - jsRules string // The rules to use -} - -func NewRuleEvaluator(next core.UIClientAPI, jsbackend storage.Storage) (*rulesetUI, error) { - c := &rulesetUI{ - next: next, - storage: jsbackend, - jsRules: "", - } - - return c, nil -} -func (r *rulesetUI) RegisterUIServer(api *core.UIServerAPI) { - r.next.RegisterUIServer(api) - // TODO, make it possible to query from js -} - -func (r *rulesetUI) Init(javascriptRules string) error { - r.jsRules = javascriptRules - return nil -} -func (r *rulesetUI) execute(jsfunc string, jsarg interface{}) (goja.Value, error) { - // Instantiate a fresh vm engine every time - vm := goja.New() - - // Set the native callbacks - consoleObj := vm.NewObject() - consoleObj.Set("log", consoleOutput) - consoleObj.Set("error", consoleOutput) - vm.Set("console", consoleObj) - - storageObj := vm.NewObject() - storageObj.Set("put", func(call goja.FunctionCall) goja.Value { - key, val := call.Argument(0).String(), call.Argument(1).String() - if val == "" { - r.storage.Del(key) - } else { - r.storage.Put(key, val) - } - return goja.Null() - }) - storageObj.Set("get", func(call goja.FunctionCall) goja.Value { - goval, _ := r.storage.Get(call.Argument(0).String()) - jsval := vm.ToValue(goval) - return jsval - }) - vm.Set("storage", storageObj) - - // Load bootstrap libraries - script, err := goja.Compile("bignumber.js", deps.BigNumberJS, true) - if err != nil { - log.Warn("Failed loading libraries", "err", err) - return goja.Undefined(), err - } - vm.RunProgram(script) - - // Run the actual rule implementation - _, err = vm.RunString(r.jsRules) - if err != nil { - log.Warn("Execution failed", "err", err) - return goja.Undefined(), err - } - - // And the actual call - // All calls are objects with the parameters being keys in that object. - // To provide additional insulation between js and go, we serialize it into JSON on the Go-side, - // and deserialize it on the JS side. - - jsonbytes, err := json.Marshal(jsarg) - if err != nil { - log.Warn("failed marshalling data", "data", jsarg) - return goja.Undefined(), err - } - // Now, we call foobar(JSON.parse()). - var call string - if len(jsonbytes) > 0 { - call = fmt.Sprintf("%v(JSON.parse(%v))", jsfunc, string(jsonbytes)) - } else { - call = fmt.Sprintf("%v()", jsfunc) - } - return vm.RunString(call) -} - -func (r *rulesetUI) checkApproval(jsfunc string, jsarg []byte, err error) (bool, error) { - if err != nil { - return false, err - } - v, err := r.execute(jsfunc, string(jsarg)) - if err != nil { - log.Info("error occurred during execution", "error", err) - return false, err - } - result := v.ToString().String() - if result == "Approve" { - log.Info("Op approved") - return true, nil - } else if result == "Reject" { - log.Info("Op rejected") - return false, nil - } - return false, errors.New("unknown response") -} - -func (r *rulesetUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { - jsonreq, err := json.Marshal(request) - approved, err := r.checkApproval("ApproveTx", jsonreq, err) - if err != nil { - log.Info("Rule-based approval error, going to manual", "error", err) - return r.next.ApproveTx(request) - } - - if approved { - return core.SignTxResponse{ - Transaction: request.Transaction, - Approved: true}, - nil - } - return core.SignTxResponse{Approved: false}, err -} - -func (r *rulesetUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { - jsonreq, err := json.Marshal(request) - approved, err := r.checkApproval("ApproveSignData", jsonreq, err) - if err != nil { - log.Info("Rule-based approval error, going to manual", "error", err) - return r.next.ApproveSignData(request) - } - if approved { - return core.SignDataResponse{Approved: true}, nil - } - return core.SignDataResponse{Approved: false}, err -} - -// OnInputRequired not handled by rules -func (r *rulesetUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) { - return r.next.OnInputRequired(info) -} - -func (r *rulesetUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { - jsonreq, err := json.Marshal(request) - approved, err := r.checkApproval("ApproveListing", jsonreq, err) - if err != nil { - log.Info("Rule-based approval error, going to manual", "error", err) - return r.next.ApproveListing(request) - } - if approved { - return core.ListResponse{Accounts: request.Accounts}, nil - } - return core.ListResponse{}, err -} - -func (r *rulesetUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { - // This cannot be handled by rules, requires setting a password - // dispatch to next - return r.next.ApproveNewAccount(request) -} - -func (r *rulesetUI) ShowError(message string) { - log.Error(message) - r.next.ShowError(message) -} - -func (r *rulesetUI) ShowInfo(message string) { - log.Info(message) - r.next.ShowInfo(message) -} - -func (r *rulesetUI) OnSignerStartup(info core.StartupInfo) { - jsonInfo, err := json.Marshal(info) - if err != nil { - log.Warn("failed marshalling data", "data", info) - return - } - r.next.OnSignerStartup(info) - _, err = r.execute("OnSignerStartup", string(jsonInfo)) - if err != nil { - log.Info("error occurred during execution", "error", err) - } -} - -func (r *rulesetUI) OnApprovedTx(tx ethapi.SignTransactionResult) { - jsonTx, err := json.Marshal(tx) - if err != nil { - log.Warn("failed marshalling transaction", "tx", tx) - return - } - _, err = r.execute("OnApprovedTx", string(jsonTx)) - if err != nil { - log.Info("error occurred during execution", "error", err) - } -} diff --git a/signer/rules/rules_test.go b/signer/rules/rules_test.go deleted file mode 100644 index d27de22b29..0000000000 --- a/signer/rules/rules_test.go +++ /dev/null @@ -1,626 +0,0 @@ -// Copyright 2018 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package rules - -import ( - "fmt" - "math/big" - "strings" - "testing" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/signer/core" - "github.com/ethereum/go-ethereum/signer/core/apitypes" - "github.com/ethereum/go-ethereum/signer/storage" -) - -const JS = ` -/** -This is an example implementation of a Javascript rule file. - -When the signer receives a request over the external API, the corresponding method is evaluated. -Three things can happen: - -1. The method returns "Approve". This means the operation is permitted. -2. The method returns "Reject". This means the operation is rejected. -3. Anything else; other return values [*], method not implemented or exception occurred during processing. This means -that the operation will continue to manual processing, via the regular UI method chosen by the user. - -[*] Note: Future version of the ruleset may use more complex json-based return values, making it possible to not -only respond Approve/Reject/Manual, but also modify responses. For example, choose to list only one, but not all -accounts in a list-request. The points above will continue to hold for non-json based responses ("Approve"/"Reject"). - -**/ - -function ApproveListing(request){ - console.log("In js approve listing"); - console.log(request.accounts[3].Address) - console.log(request.meta.Remote) - return "Approve" -} - -function ApproveTx(request){ - console.log("test"); - console.log("from"); - return "Reject"; -} - -function test(thing){ - console.log(thing.String()) -} - -` - -func mixAddr(a string) (*common.MixedcaseAddress, error) { - return common.NewMixedcaseAddressFromString(a) -} - -type alwaysDenyUI struct{} - -func (alwaysDenyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) { - return core.UserInputResponse{}, nil -} -func (alwaysDenyUI) RegisterUIServer(api *core.UIServerAPI) { -} - -func (alwaysDenyUI) OnSignerStartup(info core.StartupInfo) { -} - -func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { - return core.SignTxResponse{Transaction: request.Transaction, Approved: false}, nil -} - -func (alwaysDenyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { - return core.SignDataResponse{Approved: false}, nil -} - -func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { - return core.ListResponse{Accounts: nil}, nil -} - -func (alwaysDenyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { - return core.NewAccountResponse{Approved: false}, nil -} - -func (alwaysDenyUI) ShowError(message string) { - panic("implement me") -} - -func (alwaysDenyUI) ShowInfo(message string) { - panic("implement me") -} - -func (alwaysDenyUI) OnApprovedTx(tx ethapi.SignTransactionResult) { - panic("implement me") -} - -func initRuleEngine(js string) (*rulesetUI, error) { - r, err := NewRuleEvaluator(&alwaysDenyUI{}, storage.NewEphemeralStorage()) - if err != nil { - return nil, fmt.Errorf("failed to create js engine: %v", err) - } - if err = r.Init(js); err != nil { - return nil, fmt.Errorf("failed to load bootstrap js: %v", err) - } - return r, nil -} - -func TestListRequest(t *testing.T) { - t.Parallel() - accs := make([]accounts.Account, 5) - - for i := range accs { - addr := fmt.Sprintf("000000000000000000000000000000000000000%x", i) - acc := accounts.Account{ - Address: common.BytesToAddress(common.Hex2Bytes(addr)), - URL: accounts.URL{Scheme: "test", Path: fmt.Sprintf("acc-%d", i)}, - } - accs[i] = acc - } - - js := `function ApproveListing(){ return "Approve" }` - - r, err := initRuleEngine(js) - if err != nil { - t.Errorf("Couldn't create evaluator %v", err) - return - } - resp, _ := r.ApproveListing(&core.ListRequest{ - Accounts: accs, - Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, - }) - if len(resp.Accounts) != len(accs) { - t.Errorf("Expected check to resolve to 'Approve'") - } -} - -func TestSignTxRequest(t *testing.T) { - t.Parallel() - js := ` - function ApproveTx(r){ - console.log("transaction.from", r.transaction.from); - console.log("transaction.to", r.transaction.to); - console.log("transaction.value", r.transaction.value); - console.log("transaction.nonce", r.transaction.nonce); - if(r.transaction.from.toLowerCase()=="0x0000000000000000000000000000000000001337"){ return "Approve"} - if(r.transaction.from.toLowerCase()=="0x000000000000000000000000000000000000dead"){ return "Reject"} - }` - - r, err := initRuleEngine(js) - if err != nil { - t.Errorf("Couldn't create evaluator %v", err) - return - } - to, err := mixAddr("000000000000000000000000000000000000dead") - if err != nil { - t.Error(err) - return - } - from, err := mixAddr("0000000000000000000000000000000000001337") - - if err != nil { - t.Error(err) - return - } - t.Logf("to %v", to.Address().String()) - resp, err := r.ApproveTx(&core.SignTxRequest{ - Transaction: apitypes.SendTxArgs{ - From: *from, - To: to}, - Callinfo: nil, - Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, - }) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - if !resp.Approved { - t.Errorf("Expected check to resolve to 'Approve'") - } -} - -type dummyUI struct { - calls []string -} - -func (d *dummyUI) RegisterUIServer(api *core.UIServerAPI) { - panic("implement me") -} - -func (d *dummyUI) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) { - d.calls = append(d.calls, "OnInputRequired") - return core.UserInputResponse{}, nil -} - -func (d *dummyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { - d.calls = append(d.calls, "ApproveTx") - return core.SignTxResponse{}, core.ErrRequestDenied -} - -func (d *dummyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { - d.calls = append(d.calls, "ApproveSignData") - return core.SignDataResponse{}, core.ErrRequestDenied -} - -func (d *dummyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { - d.calls = append(d.calls, "ApproveListing") - return core.ListResponse{}, core.ErrRequestDenied -} - -func (d *dummyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { - d.calls = append(d.calls, "ApproveNewAccount") - return core.NewAccountResponse{}, core.ErrRequestDenied -} - -func (d *dummyUI) ShowError(message string) { - d.calls = append(d.calls, "ShowError") -} - -func (d *dummyUI) ShowInfo(message string) { - d.calls = append(d.calls, "ShowInfo") -} - -func (d *dummyUI) OnApprovedTx(tx ethapi.SignTransactionResult) { - d.calls = append(d.calls, "OnApprovedTx") -} - -func (d *dummyUI) OnSignerStartup(info core.StartupInfo) { -} - -// TestForwarding tests that the rule-engine correctly dispatches requests to the next caller -func TestForwarding(t *testing.T) { - t.Parallel() - js := "" - ui := &dummyUI{make([]string, 0)} - jsBackend := storage.NewEphemeralStorage() - r, err := NewRuleEvaluator(ui, jsBackend) - if err != nil { - t.Fatalf("Failed to create js engine: %v", err) - } - if err = r.Init(js); err != nil { - t.Fatalf("Failed to load bootstrap js: %v", err) - } - r.ApproveSignData(nil) - r.ApproveTx(nil) - r.ApproveNewAccount(nil) - r.ApproveListing(nil) - r.ShowError("test") - r.ShowInfo("test") - - //This one is not forwarded - r.OnApprovedTx(ethapi.SignTransactionResult{}) - - expCalls := 6 - if len(ui.calls) != expCalls { - t.Errorf("Expected %d forwarded calls, got %d: %s", expCalls, len(ui.calls), strings.Join(ui.calls, ",")) - } -} - -func TestMissingFunc(t *testing.T) { - t.Parallel() - r, err := initRuleEngine(JS) - if err != nil { - t.Errorf("Couldn't create evaluator %v", err) - return - } - - _, err = r.execute("MissingMethod", "test") - - if err == nil { - t.Error("Expected error") - } - - approved, err := r.checkApproval("MissingMethod", nil, nil) - if err == nil { - t.Errorf("Expected missing method to yield error'") - } - if approved { - t.Errorf("Expected missing method to cause non-approval") - } - t.Logf("Err %v", err) -} -func TestStorage(t *testing.T) { - t.Parallel() - js := ` - function testStorage(){ - storage.put("mykey", "myvalue") - a = storage.get("mykey") - - storage.put("mykey", ["a", "list"]) // Should result in "a,list" - a += storage.get("mykey") - - - storage.put("mykey", {"an": "object"}) // Should result in "[object Object]" - a += storage.get("mykey") - - - storage.put("mykey", JSON.stringify({"an": "object"})) // Should result in '{"an":"object"}' - a += storage.get("mykey") - - a += storage.get("missingkey") //Missing keys should result in empty string - storage.put("","missing key==noop") // Can't store with 0-length key - a += storage.get("") // Should result in '' - - var b = new BigNumber(2) - var c = new BigNumber(16)//"0xf0",16) - var d = b.plus(c) - console.log(d) - return a - } -` - r, err := initRuleEngine(js) - if err != nil { - t.Errorf("Couldn't create evaluator %v", err) - return - } - - v, err := r.execute("testStorage", nil) - - if err != nil { - t.Errorf("Unexpected error %v", err) - } - retval := v.ToString().String() - - if err != nil { - t.Errorf("Unexpected error %v", err) - } - exp := `myvaluea,list[object Object]{"an":"object"}` - if retval != exp { - t.Errorf("Unexpected data, expected '%v', got '%v'", exp, retval) - } - t.Logf("Err %v", err) -} - -const ExampleTxWindow = ` - function big(str){ - if(str.slice(0,2) == "0x"){ return new BigNumber(str.slice(2),16)} - return new BigNumber(str) - } - - // Time window: 1 week - var window = 1000* 3600*24*7; - - // Limit : 1 ether - var limit = new BigNumber("1e18"); - - function isLimitOk(transaction){ - var value = big(transaction.value) - // Start of our window function - var windowstart = new Date().getTime() - window; - - var txs = []; - var stored = storage.get('txs'); - - if(stored != ""){ - txs = JSON.parse(stored) - } - // First, remove all that have passed out of the time-window - var newtxs = txs.filter(function(tx){return tx.tstamp > windowstart}); - console.log(txs, newtxs.length); - - // Secondly, aggregate the current sum - sum = new BigNumber(0) - - sum = newtxs.reduce(function(agg, tx){ return big(tx.value).plus(agg)}, sum); - console.log("ApproveTx > Sum so far", sum); - console.log("ApproveTx > Requested", value.toNumber()); - - // Would we exceed weekly limit ? - return sum.plus(value).lt(limit) - - } - function ApproveTx(r){ - console.log(r) - console.log(typeof(r)) - if (isLimitOk(r.transaction)){ - return "Approve" - } - return "Nope" - } - - /** - * OnApprovedTx(str) is called when a transaction has been approved and signed. The parameter - * 'response_str' contains the return value that will be sent to the external caller. - * The return value from this method is ignore - the reason for having this callback is to allow the - * ruleset to keep track of approved transactions. - * - * When implementing rate-limited rules, this callback should be used. - * If a rule responds with neither 'Approve' nor 'Reject' - the tx goes to manual processing. If the user - * then accepts the transaction, this method will be called. - * - * TLDR; Use this method to keep track of signed transactions, instead of using the data in ApproveTx. - */ - function OnApprovedTx(resp){ - var value = big(resp.tx.value) - var txs = [] - // Load stored transactions - var stored = storage.get('txs'); - if(stored != ""){ - txs = JSON.parse(stored) - } - // Add this to the storage - txs.push({tstamp: new Date().getTime(), value: value}); - storage.put("txs", JSON.stringify(txs)); - } - -` - -func dummyTx(value hexutil.Big) *core.SignTxRequest { - to, _ := mixAddr("000000000000000000000000000000000000dead") - from, _ := mixAddr("000000000000000000000000000000000000dead") - n := hexutil.Uint64(3) - gas := hexutil.Uint64(21000) - gasPrice := hexutil.Big(*big.NewInt(2000000)) - - return &core.SignTxRequest{ - Transaction: apitypes.SendTxArgs{ - From: *from, - To: to, - Value: value, - Nonce: n, - GasPrice: &gasPrice, - Gas: gas, - }, - Callinfo: []apitypes.ValidationInfo{ - {Typ: "Warning", Message: "All your base are belong to us"}, - }, - Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, - } -} - -func dummyTxWithV(value uint64) *core.SignTxRequest { - v := new(big.Int).SetUint64(value) - h := hexutil.Big(*v) - return dummyTx(h) -} - -func dummySigned(value *big.Int) *types.Transaction { - to := common.HexToAddress("000000000000000000000000000000000000dead") - gas := uint64(21000) - gasPrice := big.NewInt(2000000) - data := make([]byte, 0) - return types.NewTransaction(3, to, value, gas, gasPrice, data) -} - -func TestLimitWindow(t *testing.T) { - t.Parallel() - r, err := initRuleEngine(ExampleTxWindow) - if err != nil { - t.Errorf("Couldn't create evaluator %v", err) - return - } - // 0.3 ether: 429D069189E0000 wei - v := new(big.Int).SetBytes(common.Hex2Bytes("0429D069189E0000")) - h := hexutil.Big(*v) - // The first three should succeed - for i := 0; i < 3; i++ { - unsigned := dummyTx(h) - resp, err := r.ApproveTx(unsigned) - if err != nil { - t.Errorf("Unexpected error %v", err) - } - if !resp.Approved { - t.Errorf("Expected check to resolve to 'Approve'") - } - // Create a dummy signed transaction - - response := ethapi.SignTransactionResult{ - Tx: dummySigned(v), - Raw: common.Hex2Bytes("deadbeef"), - } - r.OnApprovedTx(response) - } - // Fourth should fail - resp, _ := r.ApproveTx(dummyTx(h)) - if resp.Approved { - t.Errorf("Expected check to resolve to 'Reject'") - } -} - -// dontCallMe is used as a next-handler that does not want to be called - it invokes test failure -type dontCallMe struct { - t *testing.T -} - -func (d *dontCallMe) OnInputRequired(info core.UserInputRequest) (core.UserInputResponse, error) { - d.t.Fatalf("Did not expect next-handler to be called") - return core.UserInputResponse{}, nil -} - -func (d *dontCallMe) RegisterUIServer(api *core.UIServerAPI) { -} - -func (d *dontCallMe) OnSignerStartup(info core.StartupInfo) { -} - -func (d *dontCallMe) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) { - d.t.Fatalf("Did not expect next-handler to be called") - return core.SignTxResponse{}, core.ErrRequestDenied -} - -func (d *dontCallMe) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) { - d.t.Fatalf("Did not expect next-handler to be called") - return core.SignDataResponse{}, core.ErrRequestDenied -} - -func (d *dontCallMe) ApproveListing(request *core.ListRequest) (core.ListResponse, error) { - d.t.Fatalf("Did not expect next-handler to be called") - return core.ListResponse{}, core.ErrRequestDenied -} - -func (d *dontCallMe) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) { - d.t.Fatalf("Did not expect next-handler to be called") - return core.NewAccountResponse{}, core.ErrRequestDenied -} - -func (d *dontCallMe) ShowError(message string) { - d.t.Fatalf("Did not expect next-handler to be called") -} - -func (d *dontCallMe) ShowInfo(message string) { - d.t.Fatalf("Did not expect next-handler to be called") -} - -func (d *dontCallMe) OnApprovedTx(tx ethapi.SignTransactionResult) { - d.t.Fatalf("Did not expect next-handler to be called") -} - -// TestContextIsCleared tests that the rule-engine does not retain variables over several requests. -// if it does, that would be bad since developers may rely on that to store data, -// instead of using the disk-based data storage -func TestContextIsCleared(t *testing.T) { - t.Parallel() - js := ` - function ApproveTx(){ - if (typeof foobar == 'undefined') { - foobar = "Approve" - } - console.log(foobar) - if (foobar == "Approve"){ - foobar = "Reject" - }else{ - foobar = "Approve" - } - return foobar - } - ` - ui := &dontCallMe{t} - r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage()) - if err != nil { - t.Fatalf("Failed to create js engine: %v", err) - } - if err = r.Init(js); err != nil { - t.Fatalf("Failed to load bootstrap js: %v", err) - } - tx := dummyTxWithV(0) - r1, _ := r.ApproveTx(tx) - r2, _ := r.ApproveTx(tx) - if r1.Approved != r2.Approved { - t.Errorf("Expected execution context to be cleared between executions") - } -} - -func TestSignData(t *testing.T) { - t.Parallel() - js := `function ApproveListing(){ - return "Approve" -} -function ApproveSignData(r){ - if( r.address.toLowerCase() == "0x694267f14675d7e1b9494fd8d72fefe1755710fa") - { - if(r.messages[0].value.indexOf("bazonk") >= 0){ - return "Approve" - } - return "Reject" - } - // Otherwise goes to manual processing -}` - r, err := initRuleEngine(js) - if err != nil { - t.Errorf("Couldn't create evaluator %v", err) - return - } - message := "baz bazonk foo" - hash, rawdata := accounts.TextAndHash([]byte(message)) - addr, _ := mixAddr("0x694267f14675d7e1b9494fd8d72fefe1755710fa") - - t.Logf("address %v %v\n", addr.String(), addr.Original()) - - nvt := []*apitypes.NameValueType{ - { - Name: "message", - Typ: "text/plain", - Value: message, - }, - } - resp, err := r.ApproveSignData(&core.SignDataRequest{ - Address: *addr, - Messages: nvt, - Hash: hash, - Meta: core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"}, - Rawdata: []byte(rawdata), - }) - if err != nil { - t.Fatalf("Unexpected error %v", err) - } - if !resp.Approved { - t.Fatalf("Expected approved") - } -}