Repository: ethereum/web3.py

Issue:
Warn that eth-account has not been audited
* Version: 4.0.0


## What was wrong?

`eth-account` is pending audit, but there is an expectation that `w3.eth.account` will be included with web3.py v4. We don't want people who see that v4 is going stable to mistake that as a signal that `eth-account` is ready for prod use.

## How can it be fixed?

### Do all of these:

- Include a big warning box at the top of http://web3py.readthedocs.io/en/latest/web3.eth.account.html saying something like: "This has not gone through security review"
- Some kind of explicit opt-in process
- Add a section to the docs explaining how to enable `w3.eth.account`

### Alternatives for opt-in process:

#### Approaches

1. Install "extra" to opt-in
2. Flip runtime flag to opt in
3. ??

#### Approach Detail

1. Don't install `eth-account` unless user installs web3 with `pip install web3[unaudited-private-keys]`. At startup, if `eth_account` is not available, create a `w3.eth.account` property that immediately throws on access, explaining:
    a. what the property is
    b. ask if they meant `w3.eth.accounts`
    c. why it's not enabled by default
    d. how to enable it
2. Always install `eth-account`, but immediately install the throwing `w3.eth.account` property  (see: a, b, c, d from 1). Add a `w3.eth.i_know_private_key_code_is_unaudited = True` property setter that installs `eth-account` to `w3.eth.account`.

#### Tradeoffs

1. Install "extra" to opt-in
   - Benefit: easy to get started; one-time opt-in
   - Downside: if eth-account was already installed (eg~ because they were on the beta), then they can "accidentally" opt in without noticing
2. Flip runtime flag to opt in
   - Benefit: No way to "accidentally" opt in, error message offers a straightforward solution
   - Downside: Have to flip the flag on every run, annoying at terminal

#### Current favorite

Between these, I'm leaning to 2. We can get rid off all this nonsense after the audit. In the meantime, I'd rather have it be a bit more annoying than to have people accidentally opt-in.

In the short term, for people at the terminal, we could guide them to use `from eth_account import Account` directly.

#### Something else?

@pipermerriam any other ideas or thoughts on these?


Discussion:
+1 to the long explicit property setter that enables `w3.eth.account`. 

Resolving patch:
diff --git a/conftest.py b/conftest.py
--- a/conftest.py
+++ b/conftest.py
@@ -99,4 +99,15 @@ def _wait_for_transaction(web3, txn_hash, timeout=120):
 @pytest.fixture()
 def web3():
     provider = EthereumTesterProvider()
-    return Web3(provider)
+    w3 = Web3(provider)
+
+    # Delete this whole block after eth-account has passed security audit
+    try:
+        w3.eth.account
+    except AttributeError:
+        pass
+    else:
+        raise AssertionError("Unaudited features must be disabled by default")
+    w3.eth.enable_unaudited_features()
+
+    return w3
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -83,6 +83,7 @@
     'web3.rst',
     'modules.rst',
     'web3.auto.rst',
+    'web3.gas_strategies.rst',
     'web3.middleware.rst',
     'web3.providers.rst',
     'web3.providers.eth_tester.rst',
diff --git a/web3/eth.py b/web3/eth.py
--- a/web3/eth.py
+++ b/web3/eth.py
@@ -47,13 +47,28 @@
 
 
 class Eth(Module):
-    account = Account()
+    _account = None
     defaultAccount = empty
     defaultBlock = "latest"
     defaultContractFactory = Contract
     iban = Iban
     gasPriceStrategy = None
 
+    @property
+    def account(self):
+        if self._account is not None:
+            return self._account
+        else:
+            raise AttributeError(
+                "This feature is disabled, pending security audit. "
+                "If you want to use unaudited code dealing with private keys, "
+                "despite the risks, you can run `w3.eth.enable_unaudited_features()` "
+                "and try again."
+            )
+
+    def enable_unaudited_features(self):
+        self._account = Account()
+
     def namereg(self):
         raise NotImplementedError()
 


Test patch: