mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-05-30 04:38:44 +00:00
feat(tests): Support non-bitcoinlike coins in make_payment_request.
This commit is contained in:
parent
5dd2405a86
commit
6aecf5e20e
@ -106,6 +106,7 @@ Q(apps.common.keychain)
|
|||||||
Q(apps.common.passphrase)
|
Q(apps.common.passphrase)
|
||||||
Q(apps.common.paths)
|
Q(apps.common.paths)
|
||||||
Q(apps.common.payment_request)
|
Q(apps.common.payment_request)
|
||||||
|
Q(payment_request)
|
||||||
Q(apps.common.readers)
|
Q(apps.common.readers)
|
||||||
Q(apps.common.request_pin)
|
Q(apps.common.request_pin)
|
||||||
Q(apps.common.safety_checks)
|
Q(apps.common.safety_checks)
|
||||||
|
@ -27,7 +27,7 @@ from trezorlib.tools import parse_path
|
|||||||
|
|
||||||
from .. import common
|
from .. import common
|
||||||
from .. import translations as TR
|
from .. import translations as TR
|
||||||
from ..device_tests.bitcoin.payment_req import make_coinjoin_request
|
from ..device_tests.bitcoin.coinjoin_req import make_coinjoin_request
|
||||||
from ..tx_cache import TxCache
|
from ..tx_cache import TxCache
|
||||||
from . import recovery
|
from . import recovery
|
||||||
from .common import go_next, unlock_gesture
|
from .common import go_next, unlock_gesture
|
||||||
|
26
tests/device_tests/bitcoin/coinjoin_req.py
Normal file
26
tests/device_tests/bitcoin/coinjoin_req.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from trezorlib import messages
|
||||||
|
|
||||||
|
|
||||||
|
def make_coinjoin_request(
|
||||||
|
coordinator_name,
|
||||||
|
inputs,
|
||||||
|
input_script_pubkeys,
|
||||||
|
outputs,
|
||||||
|
output_script_pubkeys,
|
||||||
|
no_fee_indices,
|
||||||
|
fee_rate=500_000, # 0.5 %
|
||||||
|
no_fee_threshold=1_000_000,
|
||||||
|
min_registrable_amount=5_000,
|
||||||
|
):
|
||||||
|
# Process inputs.
|
||||||
|
for i, txi in enumerate(inputs):
|
||||||
|
# Set no_fee flag in coinjoin_flags.
|
||||||
|
txi.coinjoin_flags |= (i in no_fee_indices) << 1
|
||||||
|
|
||||||
|
return messages.CoinJoinRequest(
|
||||||
|
fee_rate=fee_rate,
|
||||||
|
no_fee_threshold=no_fee_threshold,
|
||||||
|
min_registrable_amount=min_registrable_amount,
|
||||||
|
mask_public_key=b"",
|
||||||
|
signature=b"",
|
||||||
|
)
|
@ -25,7 +25,7 @@ from trezorlib.tools import parse_path
|
|||||||
|
|
||||||
from ...common import is_core
|
from ...common import is_core
|
||||||
from ...tx_cache import TxCache
|
from ...tx_cache import TxCache
|
||||||
from .payment_req import make_coinjoin_request
|
from .coinjoin_req import make_coinjoin_request
|
||||||
from .signtx import (
|
from .signtx import (
|
||||||
assert_tx_matches,
|
assert_tx_matches,
|
||||||
request_finished,
|
request_finished,
|
||||||
|
@ -24,7 +24,7 @@ from trezorlib.exceptions import TrezorFailure
|
|||||||
from trezorlib.tools import parse_path
|
from trezorlib.tools import parse_path
|
||||||
|
|
||||||
from ...input_flows import InputFlowPaymentRequestDetails
|
from ...input_flows import InputFlowPaymentRequestDetails
|
||||||
from .payment_req import CoinPurchaseMemo, RefundMemo, TextMemo, make_payment_request
|
from ..payment_req import CoinPurchaseMemo, RefundMemo, TextMemo, make_payment_request
|
||||||
from .signtx import forge_prevtx
|
from .signtx import forge_prevtx
|
||||||
|
|
||||||
# address at seed "all all all..." path m/84h/1h/0h/0/0
|
# address at seed "all all all..." path m/84h/1h/0h/0/0
|
||||||
@ -146,13 +146,24 @@ def test_payment_request(client: Client, payment_request_params):
|
|||||||
for i, params in enumerate(payment_request_params):
|
for i, params in enumerate(payment_request_params):
|
||||||
request_outputs = []
|
request_outputs = []
|
||||||
for txo_index in params.txo_indices:
|
for txo_index in params.txo_indices:
|
||||||
outputs[txo_index].payment_req_index = i
|
output = outputs[txo_index]
|
||||||
request_outputs.append(outputs[txo_index])
|
output.payment_req_index = i
|
||||||
|
request_outputs.append((output.amount, output.address))
|
||||||
nonce = misc.get_nonce(client) if params.get_nonce else None
|
nonce = misc.get_nonce(client) if params.get_nonce else None
|
||||||
|
for memo in params.memos:
|
||||||
|
if isinstance(memo, RefundMemo):
|
||||||
|
memo.address_resp = btc.get_authenticated_address(
|
||||||
|
client, "Testnet", memo.address_n
|
||||||
|
)
|
||||||
|
elif isinstance(memo, CoinPurchaseMemo):
|
||||||
|
memo.address_resp = btc.get_authenticated_address(
|
||||||
|
client, memo.coin_name, memo.address_n
|
||||||
|
)
|
||||||
payment_reqs.append(
|
payment_reqs.append(
|
||||||
make_payment_request(
|
make_payment_request(
|
||||||
client,
|
client,
|
||||||
recipient_name="trezor.io",
|
recipient_name="trezor.io",
|
||||||
|
slip44=1,
|
||||||
outputs=request_outputs,
|
outputs=request_outputs,
|
||||||
change_addresses=["tb1qkvwu9g3k2pdxewfqr7syz89r3gj557l3uuf9r9"],
|
change_addresses=["tb1qkvwu9g3k2pdxewfqr7syz89r3gj557l3uuf9r9"],
|
||||||
memos=params.memos,
|
memos=params.memos,
|
||||||
@ -194,7 +205,8 @@ def test_payment_request_details(client: Client):
|
|||||||
make_payment_request(
|
make_payment_request(
|
||||||
client,
|
client,
|
||||||
recipient_name="trezor.io",
|
recipient_name="trezor.io",
|
||||||
outputs=outputs[:2],
|
slip44=1,
|
||||||
|
outputs=[(txo.amount, txo.address) for txo in outputs[:2]],
|
||||||
memos=[TextMemo("Invoice #87654321.")],
|
memos=[TextMemo("Invoice #87654321.")],
|
||||||
nonce=nonce,
|
nonce=nonce,
|
||||||
)
|
)
|
||||||
@ -224,7 +236,8 @@ def test_payment_req_wrong_amount(client: Client):
|
|||||||
payment_req = make_payment_request(
|
payment_req = make_payment_request(
|
||||||
client,
|
client,
|
||||||
recipient_name="trezor.io",
|
recipient_name="trezor.io",
|
||||||
outputs=outputs[:2],
|
slip44=1,
|
||||||
|
outputs=[(txo.amount, txo.address) for txo in outputs[:2]],
|
||||||
nonce=misc.get_nonce(client),
|
nonce=misc.get_nonce(client),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -245,13 +258,15 @@ def test_payment_req_wrong_amount(client: Client):
|
|||||||
def test_payment_req_wrong_mac_refund(client: Client):
|
def test_payment_req_wrong_mac_refund(client: Client):
|
||||||
# Test wrong MAC in payment request memo.
|
# Test wrong MAC in payment request memo.
|
||||||
memo = RefundMemo(parse_path("m/44h/1h/0h/1/0"))
|
memo = RefundMemo(parse_path("m/44h/1h/0h/1/0"))
|
||||||
|
memo.address_resp = btc.get_authenticated_address(client, "Testnet", memo.address_n)
|
||||||
outputs[0].payment_req_index = 0
|
outputs[0].payment_req_index = 0
|
||||||
outputs[1].payment_req_index = 0
|
outputs[1].payment_req_index = 0
|
||||||
outputs[2].payment_req_index = None
|
outputs[2].payment_req_index = None
|
||||||
payment_req = make_payment_request(
|
payment_req = make_payment_request(
|
||||||
client,
|
client,
|
||||||
recipient_name="trezor.io",
|
recipient_name="trezor.io",
|
||||||
outputs=outputs[:2],
|
slip44=1,
|
||||||
|
outputs=[(txo.amount, txo.address) for txo in outputs[:2]],
|
||||||
memos=[memo],
|
memos=[memo],
|
||||||
nonce=misc.get_nonce(client),
|
nonce=misc.get_nonce(client),
|
||||||
)
|
)
|
||||||
@ -282,13 +297,17 @@ def test_payment_req_wrong_mac_purchase(client: Client):
|
|||||||
slip44=5,
|
slip44=5,
|
||||||
address_n=parse_path("m/44h/5h/0h/1/0"),
|
address_n=parse_path("m/44h/5h/0h/1/0"),
|
||||||
)
|
)
|
||||||
|
memo.address_resp = btc.get_authenticated_address(
|
||||||
|
client, memo.coin_name, memo.address_n
|
||||||
|
)
|
||||||
outputs[0].payment_req_index = 0
|
outputs[0].payment_req_index = 0
|
||||||
outputs[1].payment_req_index = 0
|
outputs[1].payment_req_index = 0
|
||||||
outputs[2].payment_req_index = None
|
outputs[2].payment_req_index = None
|
||||||
payment_req = make_payment_request(
|
payment_req = make_payment_request(
|
||||||
client,
|
client,
|
||||||
recipient_name="trezor.io",
|
recipient_name="trezor.io",
|
||||||
outputs=outputs[:2],
|
slip44=1,
|
||||||
|
outputs=[(txo.amount, txo.address) for txo in outputs[:2]],
|
||||||
memos=[memo],
|
memos=[memo],
|
||||||
nonce=misc.get_nonce(client),
|
nonce=misc.get_nonce(client),
|
||||||
)
|
)
|
||||||
@ -317,7 +336,8 @@ def test_payment_req_wrong_output(client: Client):
|
|||||||
payment_req = make_payment_request(
|
payment_req = make_payment_request(
|
||||||
client,
|
client,
|
||||||
recipient_name="trezor.io",
|
recipient_name="trezor.io",
|
||||||
outputs=outputs[:2],
|
slip44=1,
|
||||||
|
outputs=[(txo.amount, txo.address) for txo in outputs[:2]],
|
||||||
nonce=misc.get_nonce(client),
|
nonce=misc.get_nonce(client),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,19 +1,32 @@
|
|||||||
from collections import namedtuple
|
from dataclasses import dataclass
|
||||||
from hashlib import sha256
|
from hashlib import sha256
|
||||||
|
|
||||||
from ecdsa import SECP256k1, SigningKey
|
from ecdsa import SECP256k1, SigningKey
|
||||||
|
|
||||||
from trezorlib import btc, messages
|
from trezorlib import messages
|
||||||
|
|
||||||
from ...common import compact_size
|
from ..common import compact_size
|
||||||
|
|
||||||
SLIP44 = 1 # Testnet
|
|
||||||
|
|
||||||
TextMemo = namedtuple("TextMemo", "text")
|
@dataclass
|
||||||
RefundMemo = namedtuple("RefundMemo", "address_n")
|
class TextMemo:
|
||||||
CoinPurchaseMemo = namedtuple(
|
text: str
|
||||||
"CoinPurchaseMemo", "amount, coin_name, slip44, address_n"
|
|
||||||
)
|
|
||||||
|
@dataclass
|
||||||
|
class RefundMemo:
|
||||||
|
address_n: list[int]
|
||||||
|
address_resp: messages.Address | messages.EthereumAddress | None = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CoinPurchaseMemo:
|
||||||
|
amount: int
|
||||||
|
coin_name: str
|
||||||
|
slip44: int
|
||||||
|
address_n: list[int]
|
||||||
|
address_resp: messages.Address | messages.EthereumAddress | None = None
|
||||||
|
|
||||||
|
|
||||||
payment_req_signer = SigningKey.from_string(
|
payment_req_signer = SigningKey.from_string(
|
||||||
b"?S\ti\x8b\xc5o{,\xab\x03\x194\xea\xa8[_:\xeb\xdf\xce\xef\xe50\xf17D\x98`\xb9dj",
|
b"?S\ti\x8b\xc5o{,\xab\x03\x194\xea\xa8[_:\xeb\xdf\xce\xef\xe50\xf17D\x98`\xb9dj",
|
||||||
@ -27,7 +40,13 @@ def hash_bytes_prefixed(hasher, data):
|
|||||||
|
|
||||||
|
|
||||||
def make_payment_request(
|
def make_payment_request(
|
||||||
client, recipient_name, outputs, change_addresses=None, memos=None, nonce=None
|
client,
|
||||||
|
recipient_name,
|
||||||
|
slip44,
|
||||||
|
outputs,
|
||||||
|
change_addresses=None,
|
||||||
|
memos=None,
|
||||||
|
nonce=None,
|
||||||
):
|
):
|
||||||
h_pr = sha256(b"SL\x00\x24")
|
h_pr = sha256(b"SL\x00\x24")
|
||||||
|
|
||||||
@ -51,25 +70,19 @@ def make_payment_request(
|
|||||||
h_pr.update(memo_type.to_bytes(4, "little"))
|
h_pr.update(memo_type.to_bytes(4, "little"))
|
||||||
hash_bytes_prefixed(h_pr, memo.text.encode())
|
hash_bytes_prefixed(h_pr, memo.text.encode())
|
||||||
elif isinstance(memo, RefundMemo):
|
elif isinstance(memo, RefundMemo):
|
||||||
address_resp = btc.get_authenticated_address(
|
|
||||||
client, "Testnet", memo.address_n
|
|
||||||
)
|
|
||||||
msg_memo = messages.RefundMemo(
|
msg_memo = messages.RefundMemo(
|
||||||
address=address_resp.address, mac=address_resp.mac
|
address=memo.address_resp.address, mac=memo.address_resp.mac
|
||||||
)
|
)
|
||||||
msg_memos.append(messages.PaymentRequestMemo(refund_memo=msg_memo))
|
msg_memos.append(messages.PaymentRequestMemo(refund_memo=msg_memo))
|
||||||
memo_type = 2
|
memo_type = 2
|
||||||
h_pr.update(memo_type.to_bytes(4, "little"))
|
h_pr.update(memo_type.to_bytes(4, "little"))
|
||||||
hash_bytes_prefixed(h_pr, address_resp.address.encode())
|
hash_bytes_prefixed(h_pr, memo.address_resp.address.encode())
|
||||||
elif isinstance(memo, CoinPurchaseMemo):
|
elif isinstance(memo, CoinPurchaseMemo):
|
||||||
address_resp = btc.get_authenticated_address(
|
|
||||||
client, memo.coin_name, memo.address_n
|
|
||||||
)
|
|
||||||
msg_memo = messages.CoinPurchaseMemo(
|
msg_memo = messages.CoinPurchaseMemo(
|
||||||
coin_type=memo.slip44,
|
coin_type=memo.slip44,
|
||||||
amount=memo.amount,
|
amount=memo.amount,
|
||||||
address=address_resp.address,
|
address=memo.address_resp.address,
|
||||||
mac=address_resp.mac,
|
mac=memo.address_resp.mac,
|
||||||
)
|
)
|
||||||
msg_memos.append(messages.PaymentRequestMemo(coin_purchase_memo=msg_memo))
|
msg_memos.append(messages.PaymentRequestMemo(coin_purchase_memo=msg_memo))
|
||||||
|
|
||||||
@ -77,17 +90,18 @@ def make_payment_request(
|
|||||||
h_pr.update(memo_type.to_bytes(4, "little"))
|
h_pr.update(memo_type.to_bytes(4, "little"))
|
||||||
h_pr.update(memo.slip44.to_bytes(4, "little"))
|
h_pr.update(memo.slip44.to_bytes(4, "little"))
|
||||||
hash_bytes_prefixed(h_pr, memo.amount.encode())
|
hash_bytes_prefixed(h_pr, memo.amount.encode())
|
||||||
hash_bytes_prefixed(h_pr, address_resp.address.encode())
|
hash_bytes_prefixed(h_pr, memo.address_resp.address.encode())
|
||||||
else:
|
else:
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
h_pr.update(SLIP44.to_bytes(4, "little"))
|
h_pr.update(slip44.to_bytes(4, "little"))
|
||||||
|
|
||||||
change_address = iter(change_addresses or [])
|
change_address = iter(change_addresses or [])
|
||||||
h_outputs = sha256()
|
h_outputs = sha256()
|
||||||
for txo in outputs:
|
for amount, address in outputs:
|
||||||
h_outputs.update(txo.amount.to_bytes(8, "little"))
|
h_outputs.update(amount.to_bytes(8, "little"))
|
||||||
address = txo.address or next(change_address)
|
if not address:
|
||||||
|
address = next(change_address)
|
||||||
h_outputs.update(len(address).to_bytes(1, "little"))
|
h_outputs.update(len(address).to_bytes(1, "little"))
|
||||||
h_outputs.update(address.encode())
|
h_outputs.update(address.encode())
|
||||||
|
|
||||||
@ -95,33 +109,8 @@ def make_payment_request(
|
|||||||
|
|
||||||
return messages.PaymentRequest(
|
return messages.PaymentRequest(
|
||||||
recipient_name=recipient_name,
|
recipient_name=recipient_name,
|
||||||
amount=sum(txo.amount for txo in outputs if txo.address),
|
amount=sum(amount for amount, address in outputs if address),
|
||||||
memos=msg_memos,
|
memos=msg_memos,
|
||||||
nonce=nonce,
|
nonce=nonce,
|
||||||
signature=payment_req_signer.sign_digest_deterministic(h_pr.digest()),
|
signature=payment_req_signer.sign_digest_deterministic(h_pr.digest()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def make_coinjoin_request(
|
|
||||||
coordinator_name,
|
|
||||||
inputs,
|
|
||||||
input_script_pubkeys,
|
|
||||||
outputs,
|
|
||||||
output_script_pubkeys,
|
|
||||||
no_fee_indices,
|
|
||||||
fee_rate=500_000, # 0.5 %
|
|
||||||
no_fee_threshold=1_000_000,
|
|
||||||
min_registrable_amount=5_000,
|
|
||||||
):
|
|
||||||
# Process inputs.
|
|
||||||
for i, txi in enumerate(inputs):
|
|
||||||
# Set no_fee flag in coinjoin_flags.
|
|
||||||
txi.coinjoin_flags |= (i in no_fee_indices) << 1
|
|
||||||
|
|
||||||
return messages.CoinJoinRequest(
|
|
||||||
fee_rate=fee_rate,
|
|
||||||
no_fee_threshold=no_fee_threshold,
|
|
||||||
min_registrable_amount=min_registrable_amount,
|
|
||||||
mask_public_key=b"",
|
|
||||||
signature=b"",
|
|
||||||
)
|
|
Loading…
Reference in New Issue
Block a user