1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-10-19 14:30:31 +00:00
trezor-firmware/tests/device_tests/bitcoin/signtx.py

135 lines
3.9 KiB
Python

import os
from decimal import Decimal
from typing import Sequence, Tuple
import bitcoin
import requests
from bitcoin.core import COutPoint, CScript, CTransaction, CTxIn, CTxOut
from bitcoin.wallet import CBitcoinAddress
from trezorlib import messages
T = messages.RequestType
def request_input(n: int, tx_hash: bytes = None) -> messages.TxRequest:
return messages.TxRequest(
request_type=T.TXINPUT,
details=messages.TxRequestDetailsType(request_index=n, tx_hash=tx_hash),
)
def request_output(n: int, tx_hash: bytes = None) -> messages.TxRequest:
return messages.TxRequest(
request_type=T.TXOUTPUT,
details=messages.TxRequestDetailsType(request_index=n, tx_hash=tx_hash),
)
def request_orig_input(n: int, tx_hash: bytes) -> messages.TxRequest:
return messages.TxRequest(
request_type=T.TXORIGINPUT,
details=messages.TxRequestDetailsType(request_index=n, tx_hash=tx_hash),
)
def request_orig_output(n: int, tx_hash: bytes) -> messages.TxRequest:
return messages.TxRequest(
request_type=T.TXORIGOUTPUT,
details=messages.TxRequestDetailsType(request_index=n, tx_hash=tx_hash),
)
def request_payment_req(n):
return messages.TxRequest(
request_type=T.TXPAYMENTREQ,
details=messages.TxRequestDetailsType(request_index=n),
)
def request_meta(tx_hash: bytes) -> messages.TxRequest:
return messages.TxRequest(
request_type=T.TXMETA,
details=messages.TxRequestDetailsType(tx_hash=tx_hash),
)
def request_finished() -> messages.TxRequest:
return messages.TxRequest(request_type=T.TXFINISHED)
def request_extra_data(ofs: int, len: int, tx_hash: bytes) -> messages.TxRequest:
return messages.TxRequest(
request_type=T.TXEXTRADATA,
details=messages.TxRequestDetailsType(
tx_hash=tx_hash, extra_data_offset=ofs, extra_data_len=len
),
)
def assert_tx_matches(serialized_tx: bytes, hash_link: str, tx_hex: str = None) -> None:
"""Verifies if a transaction is correctly formed."""
tx_id = hash_link.split("/")[-1]
parsed_tx = CTransaction.deserialize(serialized_tx)
assert tx_id == parsed_tx.GetTxid()[::-1].hex()
if tx_hex:
assert serialized_tx.hex() == tx_hex
# TODO: we could probably do better than os.environ, this was the easiest solution
# (we could create a pytest option (and use config.getoption("check-on-chain")),
# but then each test would need to have access to config via function argument)
if int(os.environ.get("CHECK_ON_CHAIN", 0)):
def get_tx_hex(hash_link: str) -> str:
tx_data = requests.get(
hash_link, headers={"User-Agent": "BTC transactions test"}
).json(parse_float=Decimal)
return tx_data["hex"]
assert serialized_tx.hex() == get_tx_hex(hash_link)
def forge_prevtx(
vouts: Sequence[Tuple[str, int]], network: str = "mainnet"
) -> Tuple[bytes, messages.TransactionType]:
"""
Forge a transaction with the given vouts.
"""
bitcoin.SelectParams(network)
input = messages.TxInputType(
prev_hash=b"\x00" * 32,
prev_index=0xFFFFFFFF,
script_sig=b"\x00",
sequence=0xFFFFFFFF,
)
outputs = [
messages.TxOutputBinType(
amount=amount,
script_pubkey=bytes(CBitcoinAddress(address).to_scriptPubKey()),
)
for address, amount in vouts
]
tx = messages.TransactionType(
version=1,
inputs=[input],
bin_outputs=outputs,
lock_time=0,
)
cin = CTxIn(
COutPoint(input.prev_hash, input.prev_index),
CScript(input.script_sig),
input.sequence,
)
couts = [
CTxOut(output.amount, CScript(output.script_pubkey))
for output in tx.bin_outputs
]
txhash = CTransaction([cin], couts, tx.lock_time, tx.version).GetTxid()[::-1]
bitcoin.SelectParams("mainnet")
return txhash, tx