예제 #1
0
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)
예제 #2
0
def _get_inputs_interactive(blockbook_url):
    inputs = []
    txes = {}
    while True:
        echo()
        prev = prompt("Previous output to spend (txid:vout)",
                      type=parse_vin,
                      default="")
        if not prev:
            break
        prev_hash, prev_index = prev
        address_n = prompt("BIP-32 path to derive the key",
                           type=tools.parse_path)

        txhash = prev_hash.hex()
        tx_url = blockbook_url + txhash
        r = requests.get(tx_url)
        if not r.ok:
            raise click.ClickException(f"Failed to fetch URL: {tx_url}")

        tx_json = r.json(parse_float=decimal.Decimal)
        if "error" in tx_json:
            raise click.ClickException(f"Transaction not found: {txhash}")

        tx = btc.from_json(tx_json)
        txes[txhash] = tx
        amount = tx.bin_outputs[prev_index].amount
        echo("Input amount: {}".format(amount))

        sequence = prompt(
            "Sequence Number to use (RBF opt-in enabled by default)",
            type=int,
            default=0xFFFFFFFD,
        )
        script_type = prompt(
            "Input type",
            type=ChoiceType(INPUT_SCRIPTS),
            default=_default_script_type(address_n, INPUT_SCRIPTS),
        )
        if isinstance(script_type, str):
            script_type = INPUT_SCRIPTS[script_type]

        new_input = messages.TxInputType(
            address_n=address_n,
            prev_hash=prev_hash,
            prev_index=prev_index,
            amount=amount,
            script_type=script_type,
            sequence=sequence,
        )

        inputs.append(new_input)

    return inputs, txes
예제 #3
0
def test_coinbase_from_json():
    tx_dict = json.loads(TX_JSON_COINBASE, parse_float=Decimal)
    tx = btc.from_json(tx_dict)

    assert tx.version == 2
    assert tx.lock_time == 0
    assert len(tx.inputs) == 1
    assert len(tx.bin_outputs) == 4
    assert sum(o.amount for o in tx.bin_outputs) == 1256294353

    coinbase = tx.inputs[0]
    assert coinbase.prev_hash == b"\x00" * 32
    assert coinbase.prev_index == 2**32 - 1
    assert coinbase.script_sig.hex() == tx_dict["vin"][0]["coinbase"]
예제 #4
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)
예제 #5
0
def test_from_json():
    tx_dict = json.loads(TX_JSON_BIG, parse_float=Decimal)
    tx = btc.from_json(tx_dict)

    assert tx.version == 2
    assert tx.lock_time == 620109
    assert len(tx.inputs) == 5
    assert len(tx.bin_outputs) == 2
    assert sum(o.amount for o in tx.bin_outputs) == 2305776353

    for v, i in zip(tx_dict["vin"], tx.inputs):
        assert i.prev_hash.hex() == v["txid"]
        assert i.prev_index == v["vout"]
        assert i.script_sig.hex() == v["scriptSig"]["hex"]
        assert i.sequence == v["sequence"]

    for v, o in zip(tx_dict["vout"], tx.bin_outputs):
        assert o.amount == int(Decimal(v["value"]) * (10**8))
        assert o.script_pubkey.hex() == v["scriptPubKey"]["hex"]
예제 #6
0
def json_to_tx(tx_json):
    t = btc.from_json(tx_json)
    dip2_type = tx_json.get("type", 0)

    if t.version == 3 and dip2_type != 0:
        # It's a DIP2 special TX with payload

        if "extraPayloadSize" not in tx_json or "extraPayload" not in tx_json:
            raise ValueError("Payload data missing in DIP2 transaction")

        if tx_json["extraPayloadSize"] * 2 != len(tx_json["extraPayload"]):
            raise ValueError(
                "extra_data_len (%d) does not match calculated length (%d)" %
                (tx_json["extraPayloadSize"],
                 len(tx_json["extraPayload"]) * 2))
        t.extra_data = dash_utils.num_to_varint(
            tx_json["extraPayloadSize"]) + bytes.fromhex(
                tx_json["extraPayload"])

    # Trezor firmware doesn't understand the split of version and type, so let's mimic the
    # old serialization format
    t.version |= dip2_type << 16

    return t
예제 #7
0
def _get_inputs_interactive(
    blockbook_url: str,
) -> Tuple[List[messages.TxInputType], Dict[str, messages.TransactionType]]:
    inputs: List[messages.TxInputType] = []
    txes: Dict[str, messages.TransactionType] = {}
    while True:
        echo()
        prev = prompt("Previous output to spend (txid:vout)",
                      type=parse_vin,
                      default="")
        if not prev:
            break
        prev_hash, prev_index = prev

        txhash = prev_hash.hex()
        tx_url = blockbook_url + txhash
        r = SESSION.get(tx_url)
        if not r.ok:
            raise click.ClickException(f"Failed to fetch URL: {tx_url}")

        tx_json = r.json(parse_float=decimal.Decimal)
        if "error" in tx_json:
            raise click.ClickException(f"Transaction not found: {txhash}")

        tx = btc.from_json(tx_json)
        txes[txhash] = tx
        try:
            from_address = tx_json["vout"][prev_index]["scriptPubKey"][
                "address"]
            echo(f"From address: {from_address}")
        except Exception:
            pass
        amount = tx.bin_outputs[prev_index].amount
        echo(f"Input amount: {amount}")

        address_n = prompt("BIP-32 path to derive the key",
                           type=tools.parse_path)

        reported_type = tx_json["vout"][prev_index]["scriptPubKey"].get("type")
        if reported_type in BITCOIN_CORE_INPUT_TYPES:
            script_type = BITCOIN_CORE_INPUT_TYPES[reported_type]
            click.echo(f"Script type: {script_type.name}")
        else:
            script_type = prompt(
                "Input type",
                type=ChoiceType(INPUT_SCRIPTS),
                default=_default_script_type(address_n, INPUT_SCRIPTS),
            )
        if isinstance(script_type, str):
            script_type = INPUT_SCRIPTS[script_type]

        sequence = prompt(
            "Sequence Number to use (RBF opt-in enabled by default)",
            type=int,
            default=0xFFFFFFFD,
        )

        new_input = messages.TxInputType(
            address_n=address_n,
            prev_hash=prev_hash,
            prev_index=prev_index,
            amount=amount,
            script_type=script_type,
            sequence=sequence,
        )

        inputs.append(new_input)

    return inputs, txes
예제 #8
0
ins = [
    proto.TxInputType(address_n=tools.parse_path(in1_addr_path),
                      prev_hash=in1_prev_hash_b,
                      prev_index=in1_prev_index,
                      amount=in1_amount,
                      script_type=proto.InputScriptType.SPENDWITNESS,
                      sequence=sequence)
]
outs = [
    proto.TxOutputType(address=out1_address,
                       amount=out1_amount,
                       script_type=proto.OutputScriptType.PAYTOADDRESS)
]

txes = None
for i in ins:
    if i.script_type == proto.InputScriptType.SPENDADDRESS:
        tx = from_json(in1_prev_txn_j)
        txes = {in1_prev_hash_b: tx}
        break

_, serialized_tx = btc.sign_tx(client,
                               coin,
                               ins,
                               outs,
                               details=signtx,
                               prev_txes=txes)
client.close()
print(f'{{"hex": "{serialized_tx.hex()}"}}')
예제 #9
0
def json_to_tx(coin_data, tx_data):
    # TODO
    return from_json(tx_data)