Exemple #1
0
    def _create_bip69_tx(spendables: List[Spendable],
                         payables: List[Tuple[str, int]],
                         rbf: bool,
                         version: int = 1) -> Tx:
        spendables.sort(key=lambda utxo: (utxo.as_dict()["tx_hash_hex"],
                                          utxo.as_dict()["tx_out_index"]))

        # Create input list from utxos
        # Set sequence numbers to zero if using RBF.
        txs_in = [spendable.tx_in()
                  for spendable in spendables]  # type: List[TxIn]
        if rbf:
            logging.info("Spending with opt-in Replace by Fee! (RBF)")
            for txin in txs_in:
                txin.sequence = 0

        # Create output list from payables
        txs_out = list()  # type: List[TxOut]
        for payable in payables:
            bitcoin_address, coin_value = payable
            script = standard_tx_out_script(bitcoin_address)  # type: bytes
            txs_out.append(TxOut(coin_value, script))
        txs_out.sort(key=lambda txo: (txo.coin_value, b2h(txo.script)))

        tx = Tx(version=version, txs_in=txs_in, txs_out=txs_out)  # type: Tx
        tx.set_unspents(spendables)
        return tx
Exemple #2
0
 def test_sign_p2sh(self):
     tx_out_script = h2b(
         "76a91491b24bf9f5288532960ac687abb035127b1d28a588ac")
     script = script_for_address("1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm")
     self.assertEqual(tx_out_script, script)
     tx_out = TxOut(100, tx_out_script)
     tx = Tx(1, [TxIn(b'\1' * 32, 1)], [TxOut(100, tx_out_script)])
     tx.set_unspents([tx_out])
     hl = build_hash160_lookup([1], [secp256k1_generator])
     self.assertEqual(tx.bad_signature_count(), 1)
     tx.sign(hash160_lookup=hl)
     self.assertEqual(tx.bad_signature_count(), 0)
Exemple #3
0
def spend_sh_fund(tx_ins, wif_keys, tx_outs):
    """
    spend script hash fund
    the key point of an input comes from multisig address is that,
    its sign script is combined with several individual signs
    :param tx_ins: list with tuple(tx_id, idx, balance, address, redeem_script)
    :param wif_keys: private keys in wif format,
        technical should be the same order with the pubkey in redeem script,
        but pycoin has inner control, so here order is not mandatory
    :param tx_outs: balance, receiver_address
    :return: raw hex and tx id
    """
    _txs_in = []
    _un_spent = []
    for tx_id, idx, balance, address, _ in tx_ins:
        # must h2b_rev NOT h2b
        tx_id_b = h2b_rev(tx_id)
        _txs_in.append(TxIn(tx_id_b, idx))

        _un_spent.append(
            Spendable(
                balance,
                script_obj_from_address(address, netcodes=[NET_CODE]).script(),
                tx_id_b, idx))

    _txs_out = []
    for balance, receiver_address in tx_outs:
        _txs_out.append(
            TxOut(
                balance,
                script_obj_from_address(receiver_address,
                                        netcodes=[NET_CODE]).script()))

    version, lock_time = 1, 0
    tx = Tx(version, _txs_in, _txs_out, lock_time)
    tx.set_unspents(_un_spent)

    # construct hash160_lookup[hash160] = (secret_exponent, public_pair, compressed) for each individual key
    hash160_lookup = build_hash160_lookup(
        [Key.from_text(wif_key).secret_exponent() for wif_key in wif_keys])

    for i in range(0, len(tx_ins)):
        # you can add some conditions that if the input script is not p2sh type, not provide p2sh_lookup,
        # so that all kinds of inputs can work together
        p2sh_lookup = build_p2sh_lookup([binascii.unhexlify(tx_ins[i][-1])])
        tx.sign_tx_in(hash160_lookup,
                      i,
                      tx.unspents[i].script,
                      hash_type=SIGHASH_ALL,
                      p2sh_lookup=p2sh_lookup)

    return tx.as_hex(), tx.id()
Exemple #4
0
 def make_test_tx(self, input_script):
     previous_hash = b'\1' * 32
     txs_in = [TxIn(previous_hash, 0)]
     txs_out = [
         TxOut(
             1000,
             script_for_address(
                 Key(1, generator=secp256k1_generator).address()))
     ]
     version, lock_time = 1, 0
     tx = Tx(version, txs_in, txs_out, lock_time)
     unspents = [TxOut(1000, input_script)]
     tx.set_unspents(unspents)
     return tx
Exemple #5
0
def standard_tx(coins_from, coins_to):
    txs_in = []
    unspents = []
    for h, idx, tx_out in coins_from:
        txs_in.append(TxIn(h, idx))
        unspents.append(tx_out)

    txs_out = []
    for coin_value, address in coins_to:
        txs_out.append(TxOut(coin_value, script_for_address(address)))

    version, lock_time = 1, 0
    tx = Tx(version, txs_in, txs_out, lock_time)
    tx.set_unspents(unspents)
    return tx
Exemple #6
0
def standard_tx(coins_from, coins_to):
    txs_in = []
    unspents = []
    for h, idx, tx_out in coins_from:
        txs_in.append(TxIn(h, idx))
        unspents.append(tx_out)

    txs_out = []
    for coin_value, bitcoin_address in coins_to:
        txs_out.append(TxOut(coin_value, standard_tx_out_script(bitcoin_address)))

    version, lock_time = 1, 0
    tx = Tx(version, txs_in, txs_out, lock_time)
    tx.set_unspents(unspents)
    return tx
def tether_tx(tx_ins, private_key, send_amount, receiver):
    """
    simple usdt transaction
    here assume utxo comes from the sender and is used for mine fee
    bitcoin change will be sent back to sender address

    of course different address's utxo can be used for mine fee,
    but should be aware sender is determined by the first input in the tx
    bitcoin change can also be sent back to different address,
    but should be aware receiver is indicated by the last output address that is not the sender

    for full customization, use btc sample p2pkh_tx
    :param tx_ins: utxo from the sender
    :param private_key: private key of the same sender
    :param send_amount: (display amount) * (10 ** 8)
    :param receiver: address to receive usdt
    """
    _txs_in = []
    _un_spent = []
    total_bal = 0

    for tx_id, idx, balance, address in tx_ins:
        total_bal += balance

        # must h2b_rev NOT h2b
        tx_id_b = h2b_rev(tx_id)
        _txs_in.append(TxIn(tx_id_b, idx))

        _un_spent.append(Spendable(balance, standard_tx_out_script(address), tx_id_b, idx))

    txn_fee = estimate_p2pkh_tx_bytes(len(tx_ins), 3) * recommend_satoshi_per_byte()

    _txs_out = [TxOut(total_bal - txn_fee - 546, standard_tx_out_script(tx_ins[0][3])),
                TxOut(0, binascii.unhexlify(omni_tether_script(send_amount))),
                TxOut(546, standard_tx_out_script(receiver))]

    version, lock_time = 1, 0
    tx = Tx(version, _txs_in, _txs_out, lock_time)
    tx.set_unspents(_un_spent)

    solver = build_hash160_lookup([int(private_key, 16)] * len(tx_ins))
    signed_tx = tx.sign(solver, hash_type=SIGHASH_ALL)

    return signed_tx.as_hex(), signed_tx.id()
Exemple #8
0
    def _create_replacement_tx(self,
                               hist_obj: History,
                               version: int = 1) -> Tuple[Tx, Set[str], int]:
        """
        Builds a replacement Bitcoin transaction based on a given History
        object in order to implement opt in Replace-By-Fee.

        :param hist_obj: a History object from our tx history data
        :param version: an int representing the Tx version
        :returns: A not-fully-formed and unsigned replacement Tx object
        :raise: Raises a ValueError if tx not a spend or is already confirmed
        """
        if hist_obj.height == 0 and hist_obj.is_spend:
            old_tx = hist_obj.tx_obj  # type: Tx
            spendables = old_tx.unspents  # type: List[Spendable]
            chg_vout = None  # type: int

            in_addrs = set()  # type: Set[str]
            for utxo in spendables:
                in_addrs.add(utxo.address(self.chain.netcode))

            txs_out = list()  # type: List[TxOut]
            for i, txout in enumerate(old_tx.txs_out):
                value = None  # type: int
                if txout.coin_value / Wallet.COIN == hist_obj.value:
                    value = txout.coin_value
                else:
                    value = 0
                    chg_vout = i
                txs_out.append(TxOut(value, txout.script))

            new_tx = Tx(version=version,
                        txs_in=old_tx.txs_in,
                        txs_out=txs_out)  # type: Tx
            new_tx.set_unspents(spendables)
            return new_tx, in_addrs, chg_vout
        else:
            raise ValueError("This transaction is not replaceable")
Exemple #9
0
def spend_pkh_fund(tx_ins, in_keys, tx_outs):
    """
    p2pkh address send to p2pkh p2sh transaction
    :param tx_ins: list with tuple(tx_id, idx, balance, address)
    :param in_keys: list of private keys in hex format corresponding to each input
    :param tx_outs: balance, receiver_address
    :return: raw hex and tx id
    """
    _txs_in = []
    _un_spent = []
    for tx_id, idx, balance, address in tx_ins:
        # must h2b_rev NOT h2b
        tx_id_b = h2b_rev(tx_id)
        _txs_in.append(TxIn(tx_id_b, idx))

        _un_spent.append(
            Spendable(
                balance,
                script_obj_from_address(address, netcodes=[NET_CODE]).script(),
                tx_id_b, idx))

    _txs_out = []
    for balance, receiver_address in tx_outs:
        _txs_out.append(
            TxOut(
                balance,
                script_obj_from_address(receiver_address,
                                        netcodes=[NET_CODE]).script()))

    version, lock_time = 1, 0
    tx = Tx(version, _txs_in, _txs_out, lock_time)
    tx.set_unspents(_un_spent)

    solver = build_hash160_lookup([int(pri_hex, 16) for pri_hex in in_keys])
    tx.sign(solver, hash_type=SIGHASH_ALL)

    return tx.as_hex(), tx.id()
Exemple #10
0
    def _create_bip69_tx(spendables: List[Spendable],
                         payables: List[Tuple[str, int]],
                         rbf: bool,
                         version: int = 1) -> Tx:
        """ Create tx inputs and outputs from spendables and payables.
        Sort lexicographically and return unsigned Tx object.

        :param spendables: A list of Spendable objects
        :param payables: A list of payable tuples
        :param rbf: Replace by fee flag
        :param version: Tx format version
        :returns: Fully formed but unsigned Tx object
        """
        spendables.sort(key=lambda utxo: (utxo.as_dict()["tx_hash_hex"],
                                          utxo.as_dict()["tx_out_index"]))

        # Create input list from utxos
        # Set sequence numbers to zero if using RBF.
        txs_in = [spendable.tx_in()
                  for spendable in spendables]  # type: List[TxIn]
        if rbf:
            logging.info("Spending with opt-in Replace by Fee! (RBF)")
            for txin in txs_in:
                txin.sequence = 0

        # Create output list from payables
        txs_out = []  # type: List[TxOut]
        for payable in payables:
            bitcoin_address, coin_value = payable
            script = standard_tx_out_script(bitcoin_address)  # type: bytes
            txs_out.append(TxOut(coin_value, script))
        txs_out.sort(key=lambda txo: (txo.coin_value, b2h(txo.script)))

        tx = Tx(version=version, txs_in=txs_in, txs_out=txs_out)  # type: Tx
        tx.set_unspents(spendables)
        return tx
Exemple #11
0
    def _test_sighash_single(self, netcode):
        k0 = Key(secret_exponent=PRIV_KEYS[0],
                 is_compressed=True,
                 netcode=netcode)
        k1 = Key(secret_exponent=PRIV_KEYS[1],
                 is_compressed=True,
                 netcode=netcode)
        k2 = Key(secret_exponent=PRIV_KEYS[2],
                 is_compressed=True,
                 netcode=netcode)
        k3 = Key(secret_exponent=PRIV_KEYS[3],
                 is_compressed=True,
                 netcode=netcode)
        k4 = Key(secret_exponent=PRIV_KEYS[4],
                 is_compressed=True,
                 netcode=netcode)
        k5 = Key(secret_exponent=PRIV_KEYS[5],
                 is_compressed=True,
                 netcode=netcode)

        # Fake a coinbase transaction
        coinbase_tx = Tx.coinbase_tx(k0.sec(), 500000000)
        coinbase_tx.txs_out.append(
            TxOut(1000000000,
                  pycoin_compile('%s OP_CHECKSIG' % b2h(k1.sec()))))
        coinbase_tx.txs_out.append(
            TxOut(1000000000,
                  pycoin_compile('%s OP_CHECKSIG' % b2h(k2.sec()))))

        self.assertEqual(
            '2acbe1006f7168bad538b477f7844e53de3a31ffddfcfc4c6625276dd714155a',
            b2h_rev(coinbase_tx.hash()))

        # Make the test transaction
        txs_in = [
            TxIn(coinbase_tx.hash(), 0),
            TxIn(coinbase_tx.hash(), 1),
            TxIn(coinbase_tx.hash(), 2),
        ]
        txs_out = [
            TxOut(900000000, standard_tx_out_script(k3.address())),
            TxOut(800000000, standard_tx_out_script(k4.address())),
            TxOut(800000000, standard_tx_out_script(k5.address())),
        ]
        tx = Tx(1, txs_in, txs_out)
        tx.set_unspents(coinbase_tx.txs_out)

        self.assertEqual(
            '791b98ef0a3ac87584fe273bc65abd89821569fd7c83538ac0625a8ca85ba587',
            b2h_rev(tx.hash()))

        sig_type = SIGHASH_SINGLE

        sig_hash = tx.signature_hash(coinbase_tx.txs_out[0].script, 0,
                                     sig_type)
        self.assertEqual(
            'cc52d785a3b4133504d1af9e60cd71ca422609cb41df3a08bbb466b2a98a885e',
            b2h(to_bytes_32(sig_hash)))

        sig = sigmake(k0, sig_hash, sig_type)
        self.assertTrue(sigcheck(k0, sig_hash, sig[:-1]))

        tx.txs_in[0].script = pycoin_compile(b2h(sig))
        self.assertTrue(tx.is_signature_ok(0))

        sig_hash = tx.signature_hash(coinbase_tx.txs_out[1].script, 1,
                                     sig_type)
        self.assertEqual(
            '93bb883d70fccfba9b8aa2028567aca8357937c65af7f6f5ccc6993fd7735fb7',
            b2h(to_bytes_32(sig_hash)))

        sig = sigmake(k1, sig_hash, sig_type)
        self.assertTrue(sigcheck(k1, sig_hash, sig[:-1]))

        tx.txs_in[1].script = pycoin_compile(b2h(sig))
        self.assertTrue(tx.is_signature_ok(1))

        sig_hash = tx.signature_hash(coinbase_tx.txs_out[2].script, 2,
                                     sig_type)
        self.assertEqual(
            '53ef7f67c3541bffcf4e0d06c003c6014e2aa1fb38ff33240b3e1c1f3f8e2a35',
            b2h(to_bytes_32(sig_hash)))

        sig = sigmake(k2, sig_hash, sig_type)
        self.assertTrue(sigcheck(k2, sig_hash, sig[:-1]))

        tx.txs_in[2].script = pycoin_compile(b2h(sig))
        self.assertTrue(tx.is_signature_ok(2))

        sig_type = SIGHASH_SINGLE | SIGHASH_ANYONECANPAY

        sig_hash = tx.signature_hash(coinbase_tx.txs_out[0].script, 0,
                                     sig_type)
        self.assertEqual(
            '2003393d246a7f136692ce7ab819c6eadc54ffea38eb4377ac75d7d461144e75',
            b2h(to_bytes_32(sig_hash)))

        sig = sigmake(k0, sig_hash, sig_type)
        self.assertTrue(sigcheck(k0, sig_hash, sig[:-1]))

        tx.txs_in[0].script = pycoin_compile(b2h(sig))
        self.assertTrue(tx.is_signature_ok(0))

        sig_hash = tx.signature_hash(coinbase_tx.txs_out[1].script, 1,
                                     sig_type)
        self.assertEqual(
            'e3f469ac88e9f35e8eff0bd8ad4ad3bf899c80eb7645947d60860de4a08a35df',
            b2h(to_bytes_32(sig_hash)))

        sig = sigmake(k1, sig_hash, sig_type)
        self.assertTrue(sigcheck(k1, sig_hash, sig[:-1]))

        tx.txs_in[1].script = pycoin_compile(b2h(sig))
        self.assertTrue(tx.is_signature_ok(1))

        sig_hash = tx.signature_hash(coinbase_tx.txs_out[2].script, 2,
                                     sig_type)
        self.assertEqual(
            'bacd7c3ab79cad71807312677c1788ad9565bf3c00ab9a153d206494fb8b7e6a',
            b2h(to_bytes_32(sig_hash)))

        sig = sigmake(k2, sig_hash, sig_type)
        self.assertTrue(sigcheck(k2, sig_hash, sig[:-1]))

        tx.txs_in[2].script = pycoin_compile(b2h(sig))
        self.assertTrue(tx.is_signature_ok(2))
Exemple #12
0
    async def build_tx(self,
                       priv: str,
                       addrs: List[Tuple[str, D]],
                       split_fee=True):
        """
        We distribute fee equally on every recipient by reducing the amount
        of money they will receive. The rest will go back to the sender
        as a change.

        :param priv: WIF private key of sender -> str
        :param addrs: distribution -> [(addr1, amount1), (addr2, amount2),...]
        :return: transaction id -> str
        """
        addr = Key.from_text(priv).address()

        spendables = await self.get_spendable_list_for_addr(addr)
        addrs.append((addr, D(0)))

        txs_out = []
        for payable in addrs:
            bitcoin_address, coin_value = payable
            coin_value *= COIN
            script = standard_tx_out_script(bitcoin_address)
            txs_out.append(TxOut(coin_value, script))

        txs_in = [spendable.tx_in() for spendable in spendables]
        tx = Tx(version=1, txs_in=txs_in, txs_out=txs_out, lock_time=0)

        tx.set_unspents(spendables)

        fee = await self.calc_fee(tx)

        total_coin_value = sum(spendable.coin_value
                               for spendable in tx.unspents)
        coins_allocated = sum(tx_out.coin_value for tx_out in tx.txs_out)

        if split_fee:
            fee_per_tx_out, extra_count = divmod(fee, len(tx.txs_out) - 1)

            if coins_allocated > total_coin_value:
                raise NotEnoughAmountError(
                    'Coins allocated exceeds total spendable: '
                    f'allocated: {coins_allocated}, '
                    f'spendable: {total_coin_value}')

            for tx_out in tx.txs_out:
                if tx_out.address(netcode=self.NETCODE) == addr:
                    tx_out.coin_value = total_coin_value - coins_allocated
                else:
                    tx_out.coin_value -= fee_per_tx_out
                    if extra_count > 0:
                        tx_out.coin_value -= 1
                        extra_count -= 1
                    if tx_out.coin_value < 1:
                        raise NotEnoughAmountError(
                            'Not enough in each output to spread fee evenly: '
                            f'{tx_out.address} allocated too little')
        else:
            if (coins_allocated + fee) > total_coin_value:
                raise NotEnoughAmountError(
                    'Coins allocated exceeds total spendable: '
                    f'allocated: {coins_allocated}, '
                    f'fee: {fee}, '
                    f'spendable: {total_coin_value}')
            for tx_out in tx.txs_out:
                if tx_out.address(netcode=self.NETCODE) == addr:
                    tx_out.coin_value = (total_coin_value - coins_allocated -
                                         fee)
                    break
        return tx