I will be adding more unit tests but with functions involving cryptographic operations like deriving addresses, what do I use as my source of truth? This is something I am still struggling with.
That's a tough question. For any part, finding any test vectors or creating one yourself using the reference implementation (bitcoin core) is a good idea. For example you could create your edge case private keys, import them in core and get their public key and addresses. It also has a lot of test vectors that could be used.
I'd still continue with
separation of concerns and make testing easier. For example it has the following parts that can be tested separately:
1. From entropy to child keys (BIP32) which is computing a bunch of HMACSHA512 and some elliptic curve cryptography. Test vectors are found in
BIP322. From child private key to public key, test vectors found in NIST standards but not needed if you use a library to do the conversion.
3. From public key to byte array which is important because it must always return 33 bytes no matter what the value is (it may need padding with zeros if the x is smaller than 32 bytes).
4. From that byte[] to hash. You would be using a library here too so no need for test but tests are found in respective RFC docs or
NIST also has a lot of hash tests.
5. From hash to address which is the encoding part, most base58 libraries have a lot of test vectors (bitcoin core also has them), bech32 tests can be found in
BIP173