mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-21 13:58:08 +00:00
126 lines
3.9 KiB
Python
126 lines
3.9 KiB
Python
|
"""
|
||
|
Script for quick (and repeatable) creation and signing of
|
||
|
specific inputs and outputs inspired by BTC device tests.
|
||
|
|
||
|
INPUTS and OUTPUTS lists are the only things needed to be modified.
|
||
|
|
||
|
Similar to tools/build_tx.py, but more suitable for bigger/autogenerated transactions.
|
||
|
|
||
|
The serialized transaction can then be announced to network at https://tbtc1.trezor.io/sendtx
|
||
|
It could be useful to inspect the transaction details at https://live.blockcypher.com/btc/decodetx/
|
||
|
|
||
|
Usage:
|
||
|
- modify INPUTS and OUTPUTS lists to suit the needs
|
||
|
- call the script with possible flags - see `python sign_tx.py --help`
|
||
|
"""
|
||
|
|
||
|
import argparse
|
||
|
from decimal import Decimal
|
||
|
from typing import Dict, List
|
||
|
|
||
|
import requests
|
||
|
|
||
|
from trezorlib import btc, messages
|
||
|
from trezorlib.client import get_default_client
|
||
|
from trezorlib.debuglink import TrezorClientDebugLink
|
||
|
from trezorlib.tools import parse_path
|
||
|
from trezorlib.transport import enumerate_devices
|
||
|
|
||
|
parser = argparse.ArgumentParser()
|
||
|
parser.add_argument(
|
||
|
"--autoconfirm",
|
||
|
action="store_true",
|
||
|
help="Automatically confirm everything on the device.",
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
"--testnet",
|
||
|
action="store_true",
|
||
|
help="Use BTC testnet instead of mainnet.",
|
||
|
)
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
# Can choose autoconfirm everything on the device (in the device-tests-style)
|
||
|
# (Suitable for long/repetitive transactions)
|
||
|
if args.autoconfirm:
|
||
|
print("Autoconfirming everything on the device.")
|
||
|
for device in enumerate_devices():
|
||
|
try:
|
||
|
CLIENT = TrezorClientDebugLink(device, auto_interact=True)
|
||
|
break
|
||
|
except Exception:
|
||
|
pass
|
||
|
else:
|
||
|
raise RuntimeError("Could not find device")
|
||
|
else:
|
||
|
CLIENT = get_default_client()
|
||
|
# Choosing between Mainnet and Testnet
|
||
|
if args.testnet:
|
||
|
COIN = "Testnet"
|
||
|
URL = "https://tbtc1.trezor.io/api/tx-specific"
|
||
|
else:
|
||
|
COIN = "Bitcoin"
|
||
|
URL = "https://btc1.trezor.io/api/tx-specific"
|
||
|
print(f"Operating on {COIN} at {URL}")
|
||
|
|
||
|
# Specific example of generating and signing a transaction with 255 outputs
|
||
|
# (Could be tried on `all all all...` seed on testnet)
|
||
|
# (--autoconfirm really helps here)
|
||
|
INPUTS = [
|
||
|
messages.TxInputType(
|
||
|
address_n=parse_path("44h/1h/0h/0/0"), # mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q
|
||
|
amount=1_827_955,
|
||
|
prev_hash=bytes.fromhex(
|
||
|
"58d56a5d1325cf83543ee4c87fd73a784e4ba1499ced574be359fa2bdcb9ac8e"
|
||
|
),
|
||
|
prev_index=1,
|
||
|
),
|
||
|
]
|
||
|
count = 255
|
||
|
OUTPUTS = [
|
||
|
messages.TxOutputType(
|
||
|
address="momtnzR3XqXgDSsFmd8gkGxUiHZLde3RmA", # "44h/1h/0h/0/3"
|
||
|
amount=(1_827_955 - 10_000) // count,
|
||
|
script_type=messages.OutputScriptType.PAYTOADDRESS,
|
||
|
)
|
||
|
for _ in range(count)
|
||
|
]
|
||
|
|
||
|
|
||
|
def get_tx_info(tx_id: str) -> messages.TransactionType:
|
||
|
"""Fetch basic transaction info for the signing."""
|
||
|
tx_url = f"{URL}/{tx_id}"
|
||
|
tx_src = requests.get(tx_url, headers={"user-agent": "tx_cache"}).json(
|
||
|
parse_float=Decimal
|
||
|
)
|
||
|
if "error" in tx_src:
|
||
|
raise RuntimeError(tx_src["error"])
|
||
|
return btc.from_json(tx_src)
|
||
|
|
||
|
|
||
|
def get_prev_txes(
|
||
|
inputs: List[messages.TxInputType],
|
||
|
) -> Dict[bytes, messages.TransactionType]:
|
||
|
"""Get info for all the previous transactions inputs are depending on."""
|
||
|
prev_txes = {}
|
||
|
for input in inputs:
|
||
|
tx_id = input.prev_hash
|
||
|
if tx_id not in prev_txes:
|
||
|
prev_txes[tx_id] = get_tx_info(tx_id.hex())
|
||
|
|
||
|
return prev_txes
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
assert len(INPUTS) > 0, "there are no inputs"
|
||
|
assert len(OUTPUTS) > 0, "there are no outputs"
|
||
|
if not all(isinstance(inp, messages.TxInputType) for inp in INPUTS):
|
||
|
raise RuntimeError("all inputs must be TxInputType")
|
||
|
if not all(isinstance(out, messages.TxOutputType) for out in OUTPUTS):
|
||
|
raise RuntimeError("all outputs must be TxOutputType")
|
||
|
|
||
|
_, serialized_tx = btc.sign_tx(
|
||
|
CLIENT, COIN, INPUTS, OUTPUTS, prev_txes=get_prev_txes(INPUTS)
|
||
|
)
|
||
|
print(80 * "-")
|
||
|
print(serialized_tx.hex())
|