specter-desktop/tests/test_rest.py
2026-08-08 14:57:14 +02:00

211 lines
7.3 KiB
Python

import base64
import json
import logging
from numbers import Number
import jwt
import pytest
import datetime
import random
from flask import current_app as app
from cryptoadvance.specter.managers.device_manager import DeviceManager
from cryptoadvance.specter.specter import Specter
from cryptoadvance.specter.specter_error import SpecterError
from cryptoadvance.specter.util.wallet_importer import WalletImporter
from cryptoadvance.specter.user import User
logger = logging.getLogger(__name__)
def create_registered_jwt_token(specter, username):
user = specter.user_manager.get_user_by_username(username)
jwt_token_id = User.generate_token_id()
jwt_token_life = 3600
jwt_token = User.generate_jwt_token(
username, jwt_token_id, "test token", jwt_token_life
)
user.add_jwt_token(jwt_token_id, jwt_token, "test token", jwt_token_life)
return jwt_token
def almost_equal(a: Number, b: Number, precision: float = 0.01) -> bool:
"""
Checks if a and b are not very different.
Default precision is 1%
"""
if a == b:
return True
diff = 2 * (a - b) / (a + b)
return (diff < precision) and (diff > -precision)
def test_rr_psbt_get(client, specter_regtest_configured, bitcoin_regtest, caplog):
create_a_simple_wallet(specter_regtest_configured, bitcoin_regtest)
assert (
specter_regtest_configured.config["testing"]["allow_threading_for_testing"]
== False
) # Tests fail with threading
caplog.set_level(logging.DEBUG)
""" testing the registration """
# Unauthorized
result = client.get("/api/v1alpha/wallets/some_wallet/psbt", follow_redirects=True)
assert result.status_code == 401
assert json.loads(result.data)["message"].startswith(
"The server could not verify that you are authorized to access the URL requested."
)
# Wrong token
headers = {"Authorization": "Bearer " + "sometoken"}
result = client.get(
"/api/v1alpha/wallets/a_simple_wallet/psbt",
follow_redirects=True,
headers=headers,
)
assert result.status_code == 401
assert json.loads(result.data)["message"].startswith(
"The server could not verify that you are authorized to access the URL requested."
)
# Admin but not authorized (admin is NOT allowed to read everything)
headers = {
"Authorization": "Bearer "
+ create_registered_jwt_token(specter_regtest_configured, "admin")
}
result = client.get(
"/api/v1alpha/wallets/a_simple_wallet/psbt",
follow_redirects=True,
headers=headers,
)
assert result.status_code == 403
assert (
json.loads(result.data)["message"]
== "The wallet does not belong to the user in the request."
)
# Proper authorized (the wallet is owned by someuser)
headers = {
"Authorization": "Bearer "
+ create_registered_jwt_token(specter_regtest_configured, "someuser")
}
result = client.get(
"/api/v1alpha/wallets/a_simple_wallet/psbt",
follow_redirects=True,
headers=headers,
)
assert result.status_code == 200
data = json.loads(result.data)
assert data["result"] == {}
def test_rr_psbt_post(specter_regtest_configured, bitcoin_regtest, client, caplog):
create_a_simple_wallet(specter_regtest_configured, bitcoin_regtest)
caplog.set_level(logging.DEBUG)
""" testing the registration """
headers = {
"Authorization": "Bearer "
+ create_registered_jwt_token(specter_regtest_configured, "someuser"),
"Content-type": "application/json",
}
result = client.get(
"/api/v1alpha/wallets/a_simple_wallet/",
follow_redirects=True,
headers=headers,
)
# Why the heck does this fail?
# assert json.loads(result.data)["a_simple_wallet"]["info"]["balance"] > 0
result = client.post(
"/api/v1alpha/wallets/some_wallet/psbt",
data=dict(address="someaddress", amount=0.5),
follow_redirects=True,
)
assert result.status_code == 401
assert json.loads(result.data)["message"].startswith(
"The server could not verify that you are authorized to access the URL requested."
)
result = client.post(
"/api/v1alpha/wallets/a_simple_wallet/psbt",
data="""
{
"recipients" : [
{
"address": "bcrt1qgc6h85z43g3ss2dl5zdrzrp3ef6av4neqcqhh8",
"amount": 0.1,
"unit": "btc",
"label": "someLabel"
},
{
"address": "bcrt1q3kfetuxpxvujasww6xas94nawklvpz0e52uw8a",
"amount": 111211,
"unit": "sat",
"label": "someOtherLabel"
}
],
"rbf_tx_id": "",
"subtract_from": "0",
"fee_rate": "64",
"rbf": true
}
""",
follow_redirects=True,
headers=headers,
)
print(result.data)
assert result.status_code == 200
data = json.loads(result.data)
assert "bcrt1qgc6h85z43g3ss2dl5zdrzrp3ef6av4neqcqhh8" in data["result"]["address"]
assert "bcrt1q3kfetuxpxvujasww6xas94nawklvpz0e52uw8a" in data["result"]["address"]
assert 0.1 in data["result"]["amount"]
assert 0.00111211 in data["result"]["amount"]
assert data["result"]["tx"]
assert data["result"]["inputs"]
assert data["result"]["outputs"]
assert almost_equal(data["result"]["fee_rate"], 64)
assert data["result"]["tx_full_size"]
assert data["result"]["base64"]
assert data["result"]["time"]
assert data["result"]["sigs_count"] == 0
def create_a_simple_wallet(specter: Specter, bitcoin_regtest):
"""ToDo: Could potentially do this with a fixture but this is only relevant for this file only"""
payload = jwt.decode(
User.generate_jwt_token(
"someuser", "tokenid", "tokendescription", random.randrange(100, 200)
),
app.config["SECRET_KEY"],
algorithms=["HS256"],
)
username = payload["username"]
someuser = specter.user_manager.get_user_by_username(username)
wm = someuser.wallet_manager
assert not wm.working_folder is None
# Create a Wallet
wallet_json = '{"label": "a_simple_wallet", "blockheight": 0, "descriptor": "wpkh([1ef4e492/84h/1h/0h]tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc/0/*)#xp8lv5nr", "devices": [{"type": "trezor", "label": "trezor"}]} '
dm = someuser.device_manager
wallet_importer = WalletImporter(wallet_json, specter, device_manager=dm)
wallet_importer.create_nonexisting_signers(
dm,
{"unknown_cosigner_0_name": "trezor", "unknown_cosigner_0_type": "trezor"},
)
wallet = wallet_importer.create_wallet(wm)
try:
# fund it with some coins
bitcoin_regtest.testcoin_faucet(address=wallet.getnewaddress())
# make sure it's confirmed
bitcoin_regtest.mine()
# Realize that the wallet has funds:
wallet.update()
except SpecterError as se:
if str(se).startswith("Timeout"):
pytest.fail(
"We got a Bitcoin-RPC timeout while setting up the test, minting some coins. Test Error! Check cpu/mem utilastion and btc/elem logs!"
)
return
else:
raise se
assert wallet.fullbalance >= 20