Ejemplo n.º 1
0
def sign_interactive():
    coin = prompt("Coin name", default="Bitcoin")
    blockbook_host = prompt("Blockbook server", default="btc1.trezor.io")

    if not requests.get(f"https://{blockbook_host}/api").ok:
        raise click.ClickException("Could not connect to blockbook")

    blockbook_url = f"https://{blockbook_host}/api/tx-specific/"

    inputs, txes = _get_inputs_interactive(blockbook_url)
    outputs = _get_outputs_interactive()

    version = prompt("Transaction version", type=int, default=2)
    lock_time = prompt("Transaction locktime", type=int, default=0)

    result = {
        "coin_name": coin,
        "inputs": [to_dict(i, hexlify_bytes=True) for i in inputs],
        "outputs": [to_dict(o, hexlify_bytes=True) for o in outputs],
        "details": {
            "version": version,
            "lock_time": lock_time,
        },
        "prev_txes": {
            txhash: to_dict(txdata, hexlify_bytes=True)
            for txhash, txdata in txes.items()
        },
    }

    print(json.dumps(result, sort_keys=True, indent=2))
Ejemplo n.º 2
0
 def to_dict(self):
     return {
         "coin_name": self.coin_name,
         "details": protobuf.to_dict(self.details),
         "inputs": [protobuf.to_dict(i) for i in self.inputs],
         "outputs": [protobuf.to_dict(o) for o in self.outputs],
         "prev_txes": {
             key.hex(): protobuf.to_dict(value)
             for key, value in self.prev_txes.items()
         },
     }
def test_hexlify():
    msg = SimpleMessage(bytes=b"\xca\xfe\x00\x12\x34", unicode="žluťoučký kůň")
    converted_nohex = protobuf.to_dict(msg, hexlify_bytes=False)
    converted_hex = protobuf.to_dict(msg, hexlify_bytes=True)

    assert converted_nohex["bytes"] == b"\xca\xfe\x00\x12\x34"
    assert converted_nohex["unicode"] == "žluťoučký kůň"
    assert converted_hex["bytes"] == "cafe001234"
    assert converted_hex["unicode"] == "žluťoučký kůň"

    recovered_nohex = protobuf.dict_to_proto(SimpleMessage, converted_nohex)
    recovered_hex = protobuf.dict_to_proto(SimpleMessage, converted_hex)

    assert recovered_nohex.bytes == msg.bytes
    assert recovered_hex.bytes == msg.bytes
def test_to_dict():
    msg = SimpleMessage(
        uvarint=5,
        svarint=-13,
        bool=False,
        bytes=b"\xca\xfe\x00\xfe",
        unicode="žluťoučký kůň",
        enum=SimpleEnum.BAR,
        rep_int=[1, 2, 3],
        rep_str=["a", "b", "c"],
        rep_enum=[SimpleEnum.FOO, SimpleEnum.BAR, SimpleEnum.QUUX],
    )

    converted = protobuf.to_dict(msg)

    fields = [field.name for field in msg.FIELDS.values()]
    assert list(sorted(converted.keys())) == list(sorted(fields))

    assert converted["uvarint"] == 5
    assert converted["svarint"] == -13
    assert converted["bool"] is False
    assert converted["bytes"] == "cafe00fe"
    assert converted["unicode"] == "žluťoučký kůň"
    assert converted["enum"] == "BAR"
    assert converted["rep_int"] == [1, 2, 3]
    assert converted["rep_str"] == ["a", "b", "c"]
    assert converted["rep_enum"] == ["FOO", "BAR", "QUUX"]
def sign_interactive():
    coin = prompt("Coin name", default="Bitcoin")
    if coin in coins.tx_api:
        coin_data = coins.by_name[coin]
        txapi = coins.tx_api[coin]
    else:
        echo('Coin "%s" is not recognized.' % coin, err=True)
        echo("Supported coin types: %s" % ", ".join(coins.tx_api.keys()), err=True)
        sys.exit(1)

    inputs, txes = _get_inputs_interactive(coin_data, txapi)
    outputs = _get_outputs_interactive()

    if coin_data["bip115"]:
        current_block_height = txapi.current_height()
        # Zencash recommendation for the better protection
        block_height = current_block_height - 300
        block_hash = txapi.get_block_hash(block_height)
        # Blockhash passed in reverse order
        block_hash = block_hash[::-1]

        for output in outputs:
            output.block_hash_bip115 = block_hash
            output.block_height_bip115 = block_height

    signtx = messages.SignTx()
    signtx.version = prompt("Transaction version", type=int, default=2)
    signtx.lock_time = prompt("Transaction locktime", type=int, default=0)
    if coin == "Capricoin":
        signtx.timestamp = prompt("Transaction timestamp", type=int)

    result = {
        "coin_name": coin,
        "inputs": [to_dict(i, hexlify_bytes=True) for i in inputs],
        "outputs": [to_dict(o, hexlify_bytes=True) for o in outputs],
        "details": to_dict(signtx, hexlify_bytes=True),
        "prev_txes": {
            txhash.hex(): to_dict(txdata, hexlify_bytes=True)
            for txhash, txdata in txes.items()
        },
    }

    print(json.dumps(result, sort_keys=True, indent=2))
def test_nested_round_trip():
    msg = NestedMessage(
        scalar=9,
        nested=SimpleMessage(uvarint=4, enum=SimpleEnum.FOO),
        repeated=[
            SimpleMessage(),
            SimpleMessage(rep_enum=[SimpleEnum.BAR, SimpleEnum.BAR]),
            SimpleMessage(bytes=b"\xca\xfe"),
        ],
    )

    converted = protobuf.to_dict(msg)
    recovered = protobuf.dict_to_proto(NestedMessage, converted)

    assert msg == recovered
def test_dict_roundtrip():
    msg = SimpleMessage(
        uvarint=5,
        svarint=-13,
        bool=False,
        bytes=b"\xca\xfe\x00\xfe",
        unicode="žluťoučký kůň",
        enum=SimpleEnum.BAR,
        rep_int=[1, 2, 3],
        rep_str=["a", "b", "c"],
        rep_enum=[SimpleEnum.FOO, SimpleEnum.BAR, SimpleEnum.QUUX],
    )

    converted = protobuf.to_dict(msg)
    recovered = protobuf.dict_to_proto(SimpleMessage, converted)

    assert recovered == msg
Ejemplo n.º 8
0
def cli(tx, coin_name):
    """Add a transaction to the cache.

    \b
    Without URL, default blockbook server will be used:
    ./tests/tx_cache.py bcash bc37c28dfb467d2ecb50261387bf752a3977d7e5337915071bb4151e6b711a78
    It is also possible to specify URL explicitly:
    ./tests/tx_cache.py bcash https://bch1.trezor.io/api/tx-specific/bc37c28dfb467d2ecb50261387bf752a3977d7e5337915071bb4151e6b711a78

    The transaction will be parsed into Trezor format and saved in
    tests/txcache/<COIN_NAME>/<TXHASH>.json. Note that only Bitcoin-compatible fields
    will be filled out. If you are adding a coin with special fields (Dash, Zcash...),
    it is your responsibility to fill out the missing fields properly.
    """
    if tx.startswith("http"):
        tx_url = tx
        tx_hash = tx.split("/")[-1].lower()

    elif coin_name not in BLOCKBOOKS:
        raise click.ClickException(
            f"Could not find blockbook for {coin_name}. Please specify a full URL."
        )

    else:
        tx_hash = tx.lower()
        tx_url = BLOCKBOOKS[coin_name].format(tx)

    click.echo(f"Fetching from {tx_url}...")
    try:
        # Get transaction from Blockbook server. The servers refuse requests with an empty user agent.
        tx_src = requests.get(tx_url, headers={
            "user-agent": "tx_cache"
        }).json(parse_float=Decimal)
        tx_proto = btc.from_json(tx_src)
        tx_dict = protobuf.to_dict(tx_proto)
        tx_json = json.dumps(tx_dict, sort_keys=True, indent=2) + "\n"
    except Exception as e:
        raise click.ClickException(str(e)) from e

    cache_dir = CACHE_PATH / coin_name
    if not cache_dir.exists():
        cache_dir.mkdir()
    (cache_dir / f"{tx_hash}.json").write_text(tx_json)
    click.echo(tx_json)
def test_nested_to_dict():
    msg = NestedMessage(
        scalar=9,
        nested=SimpleMessage(uvarint=4, enum=SimpleEnum.FOO),
        repeated=[
            SimpleMessage(),
            SimpleMessage(rep_enum=[SimpleEnum.BAR, SimpleEnum.BAR]),
            SimpleMessage(bytes=b"\xca\xfe"),
        ],
    )

    converted = protobuf.to_dict(msg)
    assert converted["scalar"] == 9
    assert isinstance(converted["nested"], dict)
    assert isinstance(converted["repeated"], list)

    rep = converted["repeated"]
    assert rep[0] == {}
    assert rep[1] == {"rep_enum": ["BAR", "BAR"]}
    assert rep[2] == {"bytes": "cafe"}
def test_unknown_enum_to_dict():
    simple = SimpleMessage(enum=6000)
    converted = protobuf.to_dict(simple)
    assert converted["enum"] == 6000
Ejemplo n.º 11
0
def to_json(psbt_base64, psbt_file, coin_name):
    """Convert PSBT to Trezor-compatible JSON transaction.
    
    You can use `trezorctl btc sign-tx <file>` to sign it."""
    psbt_bytes = get_psbt_bytes(psbt_base64, psbt_file)
    header, inputs, outputs = psbt.read_psbt(psbt_bytes)

    coin = coins.by_name[coin_name]

    fingerprints = set()
    for inout in inputs + outputs:
        for path in inout.bip32_path.values():
            fingerprints.add(path.fingerprint)

    if not fingerprints:
        raise click.ClickException("Nothing to sign!")

    if len(fingerprints) > 1:
        fingerprint_str = ", ".join(f.hex() for f in fingerprints)
        raise click.ClickException(
            f"More than one signer fingerprint found: {fingerprint_str}")
        # TODO allow specifying or allow using all

    master_fingerprint = fingerprints.pop()

    details_dict = {
        "version": header.transaction.version,
        "lock_time": header.transaction.lock_time,
    }
    trezor_details = trezor.make_signing_details(header.transaction)

    trezor_inputs = []
    trezor_outputs = []
    prev_txes = {}

    for i, (tx_in,
            psbt_in) in enumerate(zip(header.transaction.inputs, inputs), 1):
        try:
            trezor_in = trezor.make_input(tx_in, psbt_in, master_fingerprint)
            trezor_inputs.append(trezor_in)
            prev_txes[tx_in.tx] = trezor.make_transaction(
                psbt_in.non_witness_utxo)
        except Exception as e:
            import traceback

            traceback.print_exc()
            raise click.ClickException(f"In input #{i}: {e}") from e

    for i, (tx_out,
            psbt_out) in enumerate(zip(header.transaction.outputs, outputs),
                                   1):
        try:
            trezor_out = trezor.make_output(tx_out, psbt_out,
                                            master_fingerprint, coin)
            trezor_outputs.append(trezor_out)
        except Exception as e:
            raise click.ClickException(f"In output #{i}: {e}") from e

    tx = dict(
        coin_name=coin_name,
        details=to_dict(trezor_details),
        inputs=[to_dict(txi) for txi in trezor_inputs],
        outputs=[to_dict(txo) for txo in trezor_outputs],
        prev_txes={key.hex(): to_dict(val)
                   for key, val in prev_txes.items()},
    )
    click.echo(json.dumps(tx, sort_keys=True, indent=2))