Repository: XRPLF/xrpl-py

Issue:
`Wallet` should throw a clearer error message when given an invalid seed
```
issuer_wallet = Wallet(seed="")
```
Results in 
```
  File "/Users/jacksonmills/Documents/bounty_submissions/xrpl-dev-portal/content/_code-samples/non-fungible-token/py/authorize-minter.py", line 21, in <module>
    issuer_wallet = Wallet(seed=seed, sequence=0)
  File "/Users/jacksonmills/Library/Caches/pypoetry/virtualenvs/xrpl-validator-dashboard-mm7G7WMB-py3.9/lib/python3.9/site-packages/xrpl/wallet/main.py", line 41, in __init__
    pk, sk = derive_keypair(self.seed, algorithm=algorithm)
  File "/Users/jacksonmills/Library/Caches/pypoetry/virtualenvs/xrpl-validator-dashboard-mm7G7WMB-py3.9/lib/python3.9/site-packages/xrpl/core/keypairs/main.py", line 69, in derive_keypair
    decoded_seed, algorithm = addresscodec.decode_seed(seed, algorithm)
  File "/Users/jacksonmills/Library/Caches/pypoetry/virtualenvs/xrpl-validator-dashboard-mm7G7WMB-py3.9/lib/python3.9/site-packages/xrpl/core/addresscodec/codec.py", line 123, in decode_seed
    decoded_result = _decode(seed, bytes(prefix))
  File "/Users/jacksonmills/Library/Caches/pypoetry/virtualenvs/xrpl-validator-dashboard-mm7G7WMB-py3.9/lib/python3.9/site-packages/xrpl/core/addresscodec/codec.py", line 61, in _decode
    decoded = base58.b58decode_check(b58_string, alphabet=XRPL_ALPHABET)
  File "/Users/jacksonmills/Library/Caches/pypoetry/virtualenvs/xrpl-validator-dashboard-mm7G7WMB-py3.9/lib/python3.9/site-packages/base58/__init__.py", line 157, in b58decode_check
    raise ValueError("Invalid checksum")
ValueError: Invalid checksum
```

Ideally this would throw an error which says something like "Attempted to initialize a Wallet with an invalid seed." to help people debug since seeds can be a bit tricky to verify by look alone. Also, not everyone will know that base58 is used during encoding/decoding seeds.

Discussion:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8d75eab..3cba044 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Added support for `XChainModifyBridge` flag maps (fixing an issue with `NFTokenCreateOffer` flag names)
 - Fixed `XChainModifyBridge` validation to allow just clearing of `MinAccountCreateAmount`
 - Currency codes with special characters not being allowed by IssuedCurrency objects.
+- Construction of Wallet throws an "Invalid Seed" error, if the secret is not decode-able.
 - Rectify the incorrect usage of a transaction flag name: Update `TF_NO_DIRECT_RIPPLE` to `TF_NO_RIPPLE_DIRECT`
 
 ## [2.5.0] - 2023-11-30
diff --git a/xrpl/wallet/main.py b/xrpl/wallet/main.py
index 5560644..7722c23 100644
--- a/xrpl/wallet/main.py
+++ b/xrpl/wallet/main.py
@@ -5,7 +5,9 @@ from __future__ import annotations
 from typing import List, Optional, Type
 
 from xrpl.constants import CryptoAlgorithm, XRPLException
+from xrpl.core import addresscodec
 from xrpl.core.addresscodec import classic_address_to_xaddress, ensure_classic_address
+from xrpl.core.addresscodec.exceptions import XRPLAddressCodecException
 from xrpl.core.keypairs import derive_classic_address, derive_keypair, generate_seed
 
 
@@ -57,12 +59,6 @@ class Wallet:
             algorithm: The algorithm used to encode the keys. Inferred from the seed if
                 not included
         """
-        self.seed = seed
-        """
-        The core value that is used to derive all other information about
-        this wallet. MUST be kept secret!
-        """
-
         if algorithm is None:
             if seed is not None and seed.startswith("sEd"):
                 wallet_algorithm = CryptoAlgorithm.ED25519
@@ -71,6 +67,30 @@ class Wallet:
         else:
             wallet_algorithm = algorithm
 
+        """
+        The core value that is used to derive all other information about
+        this wallet. MUST be kept secret!
+        """
+        # Validate the seed before initialization of Wallet object
+        if seed is not None:
+            try:
+                # We do not use the outputs of the below function, we are interested in
+                # whether it throws an exception or not
+                addresscodec.decode_seed(seed, wallet_algorithm)
+            except Exception as e:
+                raise XRPLAddressCodecException(
+                    "Attempted to initialize a Wallet with "
+                    + "an invalid seed: "
+                    + seed
+                    + ". The cryptographic algorithm"
+                    + " used is: "
+                    + wallet_algorithm
+                    + "\nError message: "
+                    + str(e)
+                )
+
+        self.seed = seed
+
         self.algorithm = wallet_algorithm
         """
         The algorithm that is used to convert the seed into its public/private keypair.


Test patch:
diff --git a/tests/unit/asyn/wallet/test_main.py b/tests/unit/asyn/wallet/test_main.py
index ba7d385..1b53b1e 100644
--- a/tests/unit/asyn/wallet/test_main.py
+++ b/tests/unit/asyn/wallet/test_main.py
@@ -1,6 +1,7 @@
 from unittest import TestCase
 
 from xrpl.constants import CryptoAlgorithm, XRPLException
+from xrpl.core.addresscodec.exceptions import XRPLAddressCodecException
 from xrpl.wallet.main import Wallet
 
 constants = {
@@ -139,6 +140,14 @@ class TestWalletMain(TestCase):
 
         _test_wallet_values(self, wallet, "seed", "secp256k1")
 
+    def test_wallet_contructor_throws_with_invalid_seed(self):
+        with self.assertRaises(XRPLAddressCodecException):
+            Wallet(
+                constants["regular_key_pair"]["secp256k1"]["public_key"],
+                constants["regular_key_pair"]["secp256k1"]["private_key"],
+                seed="abc",
+            )
+
     def test_constructor_using_regular_key_pair(self):
         wallet = Wallet(
             constants["regular_key_pair"]["secp256k1"]["public_key"],