You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
8.2 KiB
162 lines
8.2 KiB
from jmbase import hextobin |
|
from jmclient.configure import validate_address, load_test_config |
|
from jmclient import jm_single |
|
import jmbitcoin as btc |
|
from bitcointx.wallet import CCoinAddress |
|
from bitcointx import ChainParams |
|
import json |
|
import pytest |
|
import os |
|
|
|
pytestmark = pytest.mark.usefixtures("setup_regtest_bitcoind") |
|
|
|
testdir = os.path.dirname(os.path.realpath(__file__)) |
|
|
|
def address_valid_somewhere(addr): |
|
for x in ["bitcoin", "bitcoin/testnet", "bitcoin/regtest"]: |
|
btc.select_chain_params(x) |
|
if validate_address(addr)[0]: |
|
return True |
|
return False |
|
|
|
def test_non_addresses(setup_addresses): |
|
#could flesh this out with other examples |
|
res, msg = validate_address(2) |
|
assert res == False, "Incorrectly accepted number" |
|
|
|
def test_b58_invalid_addresses(setup_addresses): |
|
#none of these are valid as any kind of key or address |
|
with open(os.path.join(testdir,"base58_keys_invalid.json"), "r") as f: |
|
json_data = f.read() |
|
invalid_key_list = json.loads(json_data) |
|
for k in invalid_key_list: |
|
bad_key = k[0] |
|
res = address_valid_somewhere(bad_key) |
|
assert res == False, "Incorrectly validated address: " + bad_key |
|
|
|
def test_b58_valid_addresses(): |
|
with open(os.path.join(testdir,"base58_keys_valid.json"), "r") as f: |
|
json_data = f.read() |
|
valid_keys_list = json.loads(json_data) |
|
for a in valid_keys_list: |
|
addr, pubkey, prop_dict = a |
|
if not prop_dict["isPrivkey"]: |
|
if prop_dict["isTestnet"]: |
|
jm_single().config.set("BLOCKCHAIN", "network", "testnet") |
|
btc.select_chain_params("bitcoin/testnet") |
|
else: |
|
jm_single().config.set("BLOCKCHAIN", "network", "mainnet") |
|
btc.select_chain_params("bitcoin") |
|
#if using pytest -s ; sanity check to see what's actually being tested |
|
res, message = validate_address(addr) |
|
assert res == True, "Incorrectly failed to validate address: " + addr + " with message: " + message |
|
jm_single().config.set("BLOCKCHAIN", "network", "testnet") |
|
btc.select_chain_params("bitcoin/regtest") |
|
|
|
def test_valid_bech32_addresses(): |
|
valids = ["BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", |
|
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", |
|
# TODO these are valid bech32 addresses but rejected by bitcointx |
|
# because they are not witness version 0; add others. |
|
#"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", |
|
#"BC1SW50QA3JX3S", |
|
#"bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", |
|
"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy"] |
|
for va in valids: |
|
if va.lower()[:2] == "bc": |
|
jm_single().config.set("BLOCKCHAIN", "network", "mainnet") |
|
btc.select_chain_params("bitcoin") |
|
else: |
|
jm_single().config.set("BLOCKCHAIN", "network", "testnet") |
|
btc.select_chain_params("bitcoin/testnet") |
|
res, message = validate_address(va) |
|
assert res == True, "Incorrect failed to validate address: " + va + " with message: " + message |
|
jm_single().config.set("BLOCKCHAIN", "network", "testnet") |
|
btc.select_chain_params("bitcoin/regtest") |
|
|
|
def test_invalid_bech32_addresses(): |
|
invalids = [ |
|
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", |
|
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", |
|
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", |
|
"bc1rw5uspcuh", |
|
"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", |
|
"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", |
|
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", |
|
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", |
|
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", |
|
"bc1gmk9yu"] |
|
for iva in invalids: |
|
res = address_valid_somewhere(iva) |
|
assert res == False, "Incorrectly validated address: " + iva |
|
|
|
def test_valid_bip341_scriptpubkeys_addresses(): |
|
with ChainParams("bitcoin"): |
|
with open(os.path.join(testdir, |
|
"bip341_wallet_test_vectors.json"), "r") as f: |
|
json_data = json.loads(f.read()) |
|
for x in json_data["scriptPubKey"]: |
|
sPK = hextobin(x["expected"]["scriptPubKey"]) |
|
addr = x["expected"]["bip350Address"] |
|
res, message = validate_address(addr) |
|
assert res, message |
|
print("address {} was valid bech32m".format(addr)) |
|
# test this specific conversion because this is how |
|
# our human readable outputs work: |
|
assert str(CCoinAddress.from_scriptPubKey( |
|
btc.CScript(sPK))) == addr |
|
print("and it converts correctly from scriptPubKey: {}".format( |
|
btc.CScript(sPK))) |
|
|
|
# These tests are almost, but not quite, unnecessary: |
|
# we are testing the same failure cases, from BIP350, that |
|
# are already tested in our underlying bitcoin package bitcointx; |
|
# but this represents a sanity check that our top-layer validation |
|
# check is functioning, whatever the backend. We focus strongly on |
|
# detecting invalid encodings/address strings since that is where the |
|
# actual danger of loss exists. |
|
invalid_bech32_bech32m = [ |
|
["an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", "overall max length exceeded"], |
|
["qyrz8wqd2c9m", "No separator character"], |
|
["1qyrz8wqd2c9m", "Empty HRP"], |
|
["y1b0jsk6g", "Invalid data character"], |
|
["lt1igcx5c0", "Invalid data character"], |
|
["in1muywd", "Too short checksum"], |
|
["mm1crxm3i", "Invalid character in checksum"], |
|
["au1s5cgom", "Invalid character in checksum"], |
|
["M1VUXWEZ", "checksum calculated with uppercase form of HRP"], |
|
["16plkw9", "empty HRP"], |
|
["1p2gdwpf", "empty HRP"], |
|
["tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", "Invalid human-readable part"], |
|
["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", "Invalid checksum"], |
|
["BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", "Invalid witness version"], |
|
["bc1rw5uspcuh", "Invalid program length"], |
|
["bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", "Invalid program length"], |
|
["BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", "Invalid program length for witness version 0 (per BIP141)"], |
|
["tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", "Mixed case"], |
|
["bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", "zero padding of more than 4 bits"], |
|
["tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", "Non-zero padding in 8-to-5 conversion"], |
|
["bc1gmk9yu", "Empty data section"], |
|
["tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut", "Invalid human-readable part"], |
|
["bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", "Invalid checksum (Bech32 instead of Bech32m)"], |
|
["tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf", "Invalid checksum (Bech32 instead of Bech32m)"], |
|
["BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL", "Invalid checksum (Bech32 instead of Bech32m)"], |
|
["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh", "Invalid checksum (Bech32m instead of Bech32)"], |
|
["tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47", "Invalid checksum (Bech32m instead of Bech32)"], |
|
["bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4", "Invalid character in checksum"], |
|
["BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R", "Invalid witness version"], |
|
["bc1pw5dgrnzv", "Invalid program length (1 byte)"], |
|
["bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav", "Invalid program length (41 bytes)"], |
|
["tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq", "Mixed case"], |
|
["bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf", "zero padding of more than 4 bits"], |
|
["tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j", "Non-zero padding in 8-to-5 conversion"], |
|
["bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", "non-zero version, but bech32 (not bech32m)"], |
|
["BC1SW50QA3JX3S", "non-zero version, but bech32 (not bech32m)"], |
|
["bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "non-zero version, but bech32 (not bech32m)"]] |
|
def test_invalid_bech32m(): |
|
for case in invalid_bech32_bech32m: |
|
assert not address_valid_somewhere(case[0]) |
|
|
|
@pytest.fixture(scope="module") |
|
def setup_addresses(): |
|
load_test_config()
|
|
|