示例#1
0
def sign(txdata, signatories):
    tx = Tx.from_hex(txdata['tx'])
    for prevout_index, txin in enumerate(tx.txs_in):

        script = hex_to_bytes(txdata['prevout_scripts'][prevout_index])
        script_type = txdata['prevout_script_types'][prevout_index]

        if script_type == SEGWIT:
            value = int(txdata['prevout_values'][prevout_index])
            sighash = tx_segwit_hash(tx, prevout_index, script, value)
        else:
            sighash = to_bytes_32(
                tx.signature_hash(script, prevout_index, SIGHASH_ALL))

        signatures = [
            signatory.get_signature(sighash) for signatory in signatories
        ]

        if script_type == SEGWIT:
            tx.set_witness(prevout_index, [
                b'',
            ] + signatures + [
                script,
            ])
            txin.script = inscript.witness(script)
        else:
            txin.script = inscript.multisig(script, signatures)

    return tx
示例#2
0
def sign(txdata, signatories, args):  # BCASH
    tx = Tx.from_hex(txdata['tx'])

    # BCASH
    inp = []
    you_signings = []

    if args.recovery_mode == "2of2":

        tx.lock_time = 0  # no nloktime
        satoshi = 0

        for prevout_index, txin in enumerate(tx.txs_in):
            script_type = txdata['prevout_script_types'][prevout_index]
            if script_type == SEGWIT:
                return None

            txin.sequence = 0xffffffff  # no nloktime, rbf

            inp.append({
                "value":
                int(txdata['prevout_values'][prevout_index]),
                "script":
                txdata['prevout_scripts'][prevout_index],
                "subaccount":
                txdata['prevout_subaccounts'][prevout_index],
                "pointer":
                txdata['prevout_pointers'][prevout_index]
            })
            satoshi += inp[-1]["value"]

        tx.txs_out = tx.txs_out[0]  #slice to only one output
        assert len(tx.txs_out) == 1
        tx.txs_out[0].script = pycoin.ui.script_obj_from_address(
            args.destination_address).script()

        fee = len(tx.as_bin()) * args.default_feerate
        tx.txs_out[0].coin_value = satoshi - fee

    for prevout_index, txin in enumerate(tx.txs_in):

        script = hex_to_bytes(txdata['prevout_scripts'][prevout_index])
        script_type = txdata['prevout_script_types'][prevout_index]
        if script_type == SEGWIT:
            return None

        value = int(txdata['prevout_values'][prevout_index])
        sighash = tx_segwit_hash(tx, prevout_index, script, value)

        if args.recovery_mode == "2of2":
            # only for you - after is greenaddress
            you_sign = signatories[1].get_signature(sighash)
            you_signings.append(you_sign)
            txin.script = inscript.multisig_2_of_2(script, you_sign)
        else:
            signatures = [
                signatory.get_signature(sighash) for signatory in signatories
            ]
            txin.script = inscript.multisig(script, signatures)

    if args.recovery_mode == "2of2":
        # BCASH
        h = hex_from_bytes(sha256d(tx.as_bin()))
        logging.warning("sign tx: check same your email!")
        logging.warning("tx:" + tx.as_hex())
        logging.warning("sha256d: " + h)

        # ask code
        twofactor = {}
        if args.twofactor["email"]:
            args.conn.call("twofactor.request_email", "sign_alt_tx", {
                "txtype": "bcash",
                "sha256d": h
            })
            twofactor = {"method": "email"}
        elif args.twofactor["sms"]:
            args.conn.call("twofactor.request_sms", "sign_alt_tx", {
                "txtype": "bcash",
                "sha256d": h
            })
            twofactor = {"method": "sms"}
        elif args.twofactor["any"]:
            logging.warning("need email/sms twofactor")
            assert False, "need email/sms twofactor"

        if args.twofactor["any"]:
            twofactor["code"] = user_input(twofactor["method"] + " code:")

        # sign greenaddress
        signing = args.conn.call("vault.sign_alt_tx", tx.as_hex(), "bcash",
                                 inp, twofactor)

        for prevout_index, txin in enumerate(tx.txs_in):
            script = hex_to_bytes(txdata['prevout_scripts'][prevout_index])
            signatures = [
                hex_to_bytes(signing['signatures'][prevout_index]),
                you_signings[prevout_index]
            ]
            txin.script = inscript.multisig(script, signatures)

    return tx
示例#3
0
def build_single_utxo_signed_bch_tx(args, wallet_key, destination_address,
                                    ga_address_pointer, redeem_script_hex,
                                    tx_hash_hex, utxo_index, incoming_satoshis,
                                    total_fee_satoshis):
    '''Builds and signs a single-output Bitcoin Cash (BCH) transaction for a single UTXO.

    This method prepares a transaction that sends all BCH from a single UTXO (unspent transaction output) held by a
    2of2 GreenAddress multisig wallet to a single destination address (minus fees). If you have multiple UTXOs in your
    GreenAddress wallet (e.g. from multiple deposits to the wallet) you'll need to run the method once for each UTXO
    and broadcast each resulting TX separately.

    Very briefly, a UTXO represents some BTC (or BCH) that is currently held by the wallet. The UTXO is defined by the
    hash of the transaction that created it (tx_hash_hex) and the index of the output in the list of outputs for that
    transaction (utxo_index). If you're not clear on what "outputs" or UTXOs are, please spend a couple minutes
    familiarizing yourself with Bitcoin/BCH transaction basics, https://bitcoin.org/en/developer-guide#transactions or
    other references below, before trying to manually craft these parameters. I've documented them below, but some
    broader context is going to be helpful.

    This method is primarily intended for helping recover BCH associated with BTC you had in your GreenAddress wallet at
    the time of the August 1, 2017 hard fork that created BCH, but should also be usable if you accidentally sent BCH
    (instead of BTC) to a GreenAddress address post-fork.

    This method currently does not support:
        * 2of3 wallets
        * GreenAddress subaccounts
    Support for either of these should be manageable to add, so please contact me if it would be useful for you.

    References:
        P2SH multisigs: https://bitcoin.org/en/developer-guide#multisig
        OP_CHECKMULTISIG details: https://bitcoin.org/en/developer-reference#term-op-checkmultisig
        Opcodes: https://en.bitcoin.it/wiki/Script#Opcodes
        Detailed multisig TX breakdown (great if you want to parse through your TX or signature script):
            http://www.soroushjp.com/2014/12/20/bitcoin-multisig-the-hard-way-understanding-raw-multisignature-bitcoin-transactions/
        Decode a transaction (does not break down the scripts): https://blockchain.info/decode-tx
        Glossary entries:
            https://bitcoin.org/en/developer-guide#transactions
            https://bitcoin.org/en/glossary/address
            https://bitcoin.org/en/glossary/signature-script
            https://bitcoin.org/en/glossary/pubkey-script
            https://bitcoin.org/en/glossary/public-key
            https://bitcoin.org/en/glossary/output
            https://bitcoin.org/en/glossary/unspent-transaction-output
        My pre-fork and post-fork transactions from which I'm pulling my examples:
            Pre:    https://btc.com/e65e67cef4078f0a44f46bd1740c21e3dc8577c90c71e49fea876cb7bf135b70
            Post:   https://btc.com/a922ab0a66e66afe2ddb3141d88f8304e79c134e0bce8178823658b88b0e2b7a

    Args:
        args: Command-line args.

        wallet_key (int):
            GreenAddress wallet key, as returned by bip32_key_from_seed in
            https://github.com/ElementsProject/libwally-core/blob/master/include/wally_bip32.h

        destination_address (string):
            Standard-format BCH address to which you want to send the recovered BCH.
            Example: 19JRdfanvKzU7d6KvKgGTYar6kzBDba6Jn (author's BCH address -- use your own)
            These are generally base58check-encoded hashes. See https://bitcoin.org/en/glossary/address

        ga_address_pointer (int):
            GreenAddress's internal pointer to the address used by the UTXO (not destination address). This number let's
            GreenAddress know which keys to use when signing the transaction. You can find this by looking at the output
            of calls to GreenAddress's addressbook.get_my_addresses API method, and finding the entry that matches your
            UTXO's address. Other recovery tools pull this from the 'prev_pointers' field in nlocktimes.zip.

            Example:
                In my case, GreenAddress returned the following record (among others) when I called get_my_addresses.
                ga_address: {u'pointer': 15, u'addr_type': u'p2sh', u'num_tx': 2, u'ad': u'3FQBSLAsFF4NMuiEVShMae7Uu9JcVSRQvA'}
                Since 3FQBSLAsFF4NMuiEVShMae7Uu9JcVSRQvA was the address holding my UTXO, I'd set ga_address_pointer = 15.

        redeem_script_hex (string):
            Hex string encoding of the full redeem script for the UTXO (unspent transaction) you're trying
            to spend. Since we're extracting from a 2of2 P2SH multisig wallet (GreenAddress default), this
            string should start with '52' (the OP_2 opcode that represents the first '2' in '2of2'), and end with
            '52ae' (the OP_2 opcode that represents the second '2' in '2of2' and the OP_CHECKMULTISIG opcode).
            The middle contains the two Pubkeys for which signatures are needed in order to spend the UTXO.
            The string should be roughly 142 hex characters long, but could potentially vary a bit if GreenAddress
            employs pubkeys of different lengths.

            The redeem script will form the tail of the input script in the final transaction, and will be exactly the
            same for the BCH transaction as for the BTC transaction. The rest of the signature script will differ, since
            the signatures depend on other pieces of the transaction as well.

            The redeem script for a UTXO is generally not public. There are roughly three ways to get it in our case:
                1)  Extract it from the Input Script used by a BTC transaction that spent your UTXO (BTC, not BCH. As
                    noted, it starts with '52' and ends with '52ae', and is probably just the last 142 hex characters
                    of the input script. If you use the advanced view on the blockchain.info viewer, this will be the
                    last long string under the relevant ScriptSig. See this example link, which matches up with the
                    example redeemscript hex given below.
                    https://blockchain.info/tx/e65e67cef4078f0a44f46bd1740c21e3dc8577c90c71e49fea876cb7bf135b70?show_adv=true
                2)  Extract it from the nlocktimes.zip generated when you initiated the transaction holding the UTXO.
                    For recovery, this would be your last pre-fork nlocktimes.zip, though if you already have it, you
                    may want to instead just use one of the recovery tools that depends on nlocktimes.zip, such as
                    https://github.com/dumpyourbcash/garecovery
                3)  Figure out which pointer GreenAddress is supposed to use next, and determine both public keys from
                    that. I haven't done this, but it shouldn't be necessary in any case. If the pre-fork BTC funds
                    have already been spent, you can do (1). If they haven't, you can do (2), since GreenAddress lets
                    you re-request the "latest" nlocktimes through the wallet.

            Example: 5221036a08f0f8a665da604b3b4e1330ac7172e1e5a9a3466c95fc79bb50fe39eba8a22103649737c9a453eb7efa99c106117614c90ca96211542fea013ae5fda8d913bbb252ae

        tx_hash_hex (string):
            Hex string encoding of the transaction hash for the transaction containing the UTXO you're trying to spend.
            Example: e65e67cef4078f0a44f46bd1740c21e3dc8577c90c71e49fea876cb7bf135b70

        utxo_index (int):
            Index (0-based) of the unspent transaction output in the list of outputs in the transaction you're trying to
            spend.

            Example: My pre-fork transaction had two outputs, in order:
                0: 24870700 satoshis to 12HdJmPZPNo6hyYa224aUPPw1j7fhruemZ (address of person I was paying)
                1: 18232197 satoshis to 3FQBSLAsFF4NMuiEVShMae7Uu9JcVSRQvA (new multisig address where GA put my balance)
            Since my leftovers address was second, I'd set utxo_index = 1. Had it been first, I'd set utxo_index = 0.

        incoming_satoshis (int):
            Number of satoshis (BCH * 10^8) you want to spend from the UTXO. This should generally be the full amount in
            the UTXO if it's less, you'll be implicitly giving all the leftovers to the miners as a fee.
            Example: 18232197 in my case (0.18 BCH)

        total_fee_satoshis (int):
            The total number of satoshis you want to pay to the miners as a fee. Divide total_fee_satoshis by size of
            transaction (~350 bytes) to get the fee in satoshis per byte.

            See current fees by looking at recent transactions or sites like https://bitcointicker.co/bccnetworkstats/,
            though beware the units may differ.

            I've set the default to 30000 (0.0003 BCH), which is unnecessarily high (~100 satoshis per byte), to be safe
            and to ensure the transaction get processed immediately, and because it's what I used.

    Returns:
        pycoin.tx.Tx: The fully signed transaction object.

    '''

    # The UTXO you want to spend.
    spendable = Spendable.from_dict({
        "coin_value": incoming_satoshis,
        "script_hex": redeem_script_hex,
        "tx_hash_hex": tx_hash_hex,
        "tx_out_index": utxo_index
    })

    tx = pycoin.tx.tx_utils.create_tx(spendables=[spendable],
                                      payables=[destination_address],
                                      fee=total_fee_satoshis)
    logging.info("raw unsigned tx: %s", tx.as_hex())

    # SIGNING

    # User Signature
    redeem_script = spendable.script

    # The sighash of the transaction, which is the thing that will be signed independently by the wallet and
    # GreenAddress (via their API call). The hash is independent of the TX input signature scripts, since those will
    # end up including the signatures themselves.
    tx_in_sighash = tx_segwit_hash(tx, 0, redeem_script, incoming_satoshis)

    # The user signature depends only on the private key (derived from the user's wallet mnemonic) and the sighash
    # (derived from the transaction).
    private_key = bip32_key_get_priv_key(
        bip32_key_from_parent_path(wallet_key, [1, ga_address_pointer],
                                   BIP32_FLAG_SKIP_HASH))
    user_signature = ec_sig_to_der(
        ec_sig_from_bytes(private_key, tx_in_sighash,
                          EC_FLAG_ECDSA)) + bytearray([
                              0x41,
                          ])

    # Updating the script at this point would not be necessary, except that the GreenAddress API method has expectations
    # for what the input script should look like when it signs the transaction (likely because it wants to be able to
    # return an updated version of the script, which we ignore anyway). The GreenAddress signature itself will not
    # depend on the script set here, only on the redeem script, which is passed to the API method separately.
    # If this isn't done, the API method will fail with 'Invalid signature placeholder'.
    tx.txs_in[0].script = inscript.prep_for_vault_sign_alt_tx(
        redeem_script, user_signature)

    # GreenAddress Signature
    twofactor = request_twofactor_if_needed(tx)
    vault_sign_inputs = [{
        "value": incoming_satoshis,
        "script": redeem_script_hex,
        "subaccount":
        None,  # This recovery tool currently does not support subaccounts.
        "pointer": ga_address_pointer,
    }]
    # GreenAddress needs the tx.as_hex so that it can compute the hash and return an updated input script along with
    # the signature, but we're only interested in the signature, since we're rebuilding the input script below anyway.
    ga_signatures = args.conn.call("vault.sign_alt_tx", tx.as_hex(), "bcash",
                                   vault_sign_inputs, twofactor)

    # Signature order matters, and is determined by the order of the pubkeys used when creating the redeem script, and
    # thus the hash that forms the pubkey script stored in the UTXO. Since we have the redeem script, we could check the
    # order that the pubkeys appear in the redeem script, and make sure the signatures (derived from the corresponding
    # private keys) appear in the same order. However, for now we're assuming that GreenAddress always puts their
    # signature first, which means it should appear first in the signatures list below.
    signatures = [hex_to_bytes(ga_signatures['signatures'][0]), user_signature]
    tx.txs_in[0].script = inscript.multisig(redeem_script, signatures)
    logging.warning("fully signed tx: " + tx.as_hex())

    return tx