コード例 #1
0
def fake_txn(num_ins,
             num_outs,
             master_xpub=None,
             subpath="0/%d",
             fee=10000,
             outvals=None,
             segwit_in=False,
             outstyles=['p2pkh'],
             is_testnet=False,
             change_style='p2pkh',
             partial=False,
             change_outputs=[]):

    # make various size txn's ... completely fake and pointless values
    # - but has UTXO's to match needs
    # - input total = num_inputs * 1BTC
    from pycoin.tx.Tx import Tx
    from pycoin.tx.TxIn import TxIn
    from pycoin.tx.TxOut import TxOut
    from pycoin.serialize import h2b_rev
    from struct import pack

    psbt = BasicPSBT()
    txn = Tx(2, [], [])

    # we have a key; use it to provide "plausible" value inputs
    mk = BIP32Node.from_wallet_key(master_xpub)
    xfp = mk.fingerprint()

    psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)]
    psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)]

    outputs = []

    for i in range(num_ins):
        # make a fake txn to supply each of the inputs
        # - each input is 1BTC

        # addr where the fake money will be stored.
        subkey = mk.subkey_for_path(subpath % i)
        sec = subkey.sec()
        assert len(sec) == 33, "expect compressed"
        assert subpath[0:2] == '0/'

        if partial and (i == 0):
            psbt.inputs[i].bip32_paths[sec] = b'Nope' + pack('<II', 0, i)
        else:
            psbt.inputs[i].bip32_paths[sec] = xfp + pack('<II', 0, i)

        # UTXO that provides the funding for to-be-signed txn
        supply = Tx(2, [TxIn(pack('4Q', 0xdead, 0xbeef, 0, 0), 73)], [])

        scr = bytes([0x76, 0xa9, 0x14]) + subkey.hash160() + bytes(
            [0x88, 0xac])

        supply.txs_out.append(TxOut(1E8, scr))

        with BytesIO() as fd:
            if not segwit_in:
                supply.stream(fd)
                psbt.inputs[i].utxo = fd.getvalue()
            else:
                supply.txs_out[-1].stream(fd)
                psbt.inputs[i].witness_utxo = fd.getvalue()

        spendable = TxIn(supply.hash(), 0)
        txn.txs_in.append(spendable)

    for i in range(num_outs):
        is_change = False

        # random P2PKH
        if not outstyles:
            style = ADDR_STYLES[i % len(ADDR_STYLES)]
        else:
            style = outstyles[i % len(outstyles)]

        if i in change_outputs:
            scr, act_scr, isw, pubkey, sp = make_change_addr(mk, change_style)
            psbt.outputs[i].bip32_paths[pubkey] = sp
            is_change = True
        else:
            scr = act_scr = fake_dest_addr(style)
            isw = ('w' in style)

        assert scr
        act_scr = act_scr or scr

        if isw:
            psbt.outputs[i].witness_script = scr
        elif style.endswith('sh'):
            psbt.outputs[i].redeem_script = scr

        if not outvals:
            h = TxOut(round(((1E8 * num_ins) - fee) / num_outs, 4), act_scr)
        else:
            h = TxOut(outvals[i], act_scr)

        outputs.append(
            (Decimal(h.coin_value) / Decimal(1E8), act_scr, is_change))

        txn.txs_out.append(h)

    with BytesIO() as b:
        txn.stream(b)
        psbt.txn = b.getvalue()

    rv = BytesIO()
    psbt.serialize(rv)

    return rv.getvalue(), [(n, render_address(s, is_testnet), ic)
                           for n, s, ic in outputs]
コード例 #2
0
    def doit(num_ins,
             num_outs,
             M,
             keys,
             fee=10000,
             outvals=None,
             segwit_in=False,
             outstyles=['p2pkh'],
             change_outputs=[],
             incl_xpubs=False):
        psbt = BasicPSBT()
        txn = Tx(2, [], [])

        if incl_xpubs:
            # add global header with XPUB's
            # - assumes BIP45
            for xfp, m, sk in keys:
                kk = pack('<II', xfp, 45 | 0x80000000)
                psbt.xpubs.append((sk.serialize(as_private=False), kk))

        psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)]
        psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)]

        for i in range(num_ins):
            # make a fake txn to supply each of the inputs
            # - each input is 1BTC

            # addr where the fake money will be stored.
            addr, scriptPubKey, script, details = make_ms_address(M,
                                                                  keys,
                                                                  idx=i)

            # lots of supporting details needed for p2sh inputs
            if segwit_in:
                psbt.inputs[i].witness_script = script
            else:
                psbt.inputs[i].redeem_script = script

            for pubkey, xfp_path in details:
                psbt.inputs[i].bip32_paths[pubkey] = b''.join(
                    pack('<I', j) for j in xfp_path)

            # UTXO that provides the funding for to-be-signed txn
            supply = Tx(2, [TxIn(pack('4Q', 0xdead, 0xbeef, 0, 0), 73)], [])

            supply.txs_out.append(TxOut(1E8, scriptPubKey))

            with BytesIO() as fd:
                if not segwit_in:
                    supply.stream(fd)
                    psbt.inputs[i].utxo = fd.getvalue()
                else:
                    supply.txs_out[-1].stream(fd)
                    psbt.inputs[i].witness_utxo = fd.getvalue()

            spendable = TxIn(supply.hash(), 0)
            txn.txs_in.append(spendable)

        for i in range(num_outs):
            # random P2PKH
            if not outstyles:
                style = ADDR_STYLES[i % len(ADDR_STYLES)]
            else:
                style = outstyles[i % len(outstyles)]

            if i in change_outputs:
                addr, scriptPubKey, scr, details = \
                    make_ms_address(M, keys, idx=i, addr_fmt=unmap_addr_fmt[style])

                for pubkey, xfp_path in details:
                    psbt.outputs[i].bip32_paths[pubkey] = b''.join(
                        pack('<I', j) for j in xfp_path)

                if 'w' in style:
                    psbt.outputs[i].witness_script = scr
                    if style.endswith('p2sh'):
                        psbt.outputs[i].redeem_script = b'\0\x20' + sha256(
                            scr).digest()
                elif style.endswith('sh'):
                    psbt.outputs[i].redeem_script = scr
            else:
                scr = fake_dest_addr(style)

            assert scr

            if not outvals:
                h = TxOut(round(((1E8 * num_ins) - fee) / num_outs, 4),
                          scriptPubKey)
            else:
                h = TxOut(outvals[i], scriptPubKey)

            txn.txs_out.append(h)

        with BytesIO() as b:
            txn.stream(b)
            psbt.txn = b.getvalue()

        rv = BytesIO()
        psbt.serialize(rv)
        assert rv.tell() <= MAX_TXN_LEN, 'too fat'

        return rv.getvalue()
コード例 #3
0
def main():
    parser = create_parser()
    args = parser.parse_args()

    (txs, spendables, payables, key_iters, p2sh_lookup, tx_db,
     warning_tx_cache, warning_tx_for_tx_hash,
     warning_spendables) = parse_context(args, parser)

    txs_in = []
    txs_out = []
    unspents = []

    # we use a clever trick here to keep each tx_in corresponding with its tx_out
    for tx in txs:
        smaller = min(len(tx.txs_in), len(tx.txs_out))
        txs_in.extend(tx.txs_in[:smaller])
        txs_out.extend(tx.txs_out[:smaller])
        unspents.extend(tx.unspents[:smaller])
    for tx in txs:
        smaller = min(len(tx.txs_in), len(tx.txs_out))
        txs_in.extend(tx.txs_in[smaller:])
        txs_out.extend(tx.txs_out[smaller:])
        unspents.extend(tx.unspents[smaller:])
    for spendable in spendables:
        txs_in.append(spendable.tx_in())
        unspents.append(spendable)
    for address, coin_value in payables:
        script = standard_tx_out_script(address)
        txs_out.append(TxOut(coin_value, script))

    lock_time = args.lock_time
    version = args.transaction_version

    # if no lock_time is explicitly set, inherit from the first tx or use default
    if lock_time is None:
        if txs:
            lock_time = txs[0].lock_time
        else:
            lock_time = DEFAULT_LOCK_TIME

    # if no version is explicitly set, inherit from the first tx or use default
    if version is None:
        if txs:
            version = txs[0].version
        else:
            version = DEFAULT_VERSION

    if args.remove_tx_in:
        s = set(args.remove_tx_in)
        txs_in = [tx_in for idx, tx_in in enumerate(txs_in) if idx not in s]

    if args.remove_tx_out:
        s = set(args.remove_tx_out)
        txs_out = [
            tx_out for idx, tx_out in enumerate(txs_out) if idx not in s
        ]

    tx = Tx(txs_in=txs_in,
            txs_out=txs_out,
            lock_time=lock_time,
            version=version,
            unspents=unspents)

    fee = args.fee
    try:
        if len(payables) > 0:
            distribute_from_split_pool(tx, fee)
    except ValueError as ex:
        print("warning: %s" % ex.args[0], file=sys.stderr)

    unsigned_before = tx.bad_signature_count()
    unsigned_after = unsigned_before
    if unsigned_before > 0 and key_iters:

        def wif_iter(iters):
            while len(iters) > 0:
                for idx, iter in enumerate(iters):
                    try:
                        wif = next(iter)
                        yield wif
                    except StopIteration:
                        iters = iters[:idx] + iters[idx + 1:]
                        break

        print("signing...", file=sys.stderr)
        sign_tx(tx, wif_iter(key_iters), p2sh_lookup=p2sh_lookup)

        unsigned_after = tx.bad_signature_count()
        if unsigned_after > 0:
            print("warning: %d TxIn items still unsigned" % unsigned_after,
                  file=sys.stderr)

    if len(tx.txs_in) == 0:
        print("warning: transaction has no inputs", file=sys.stderr)

    if len(tx.txs_out) == 0:
        print("warning: transaction has no outputs", file=sys.stderr)

    include_unspents = (unsigned_after > 0)
    tx_as_hex = tx.as_hex(include_unspents=include_unspents)

    if args.output_file:
        f = args.output_file
        if f.name.endswith(".hex"):
            f.write(tx_as_hex.encode("utf8"))
        else:
            tx.stream(f)
            if include_unspents:
                tx.stream_unspents(f)
        f.close()
    elif args.show_unspents:
        for spendable in tx.tx_outs_as_spendable():
            print(spendable.as_text())
    else:
        if not tx.missing_unspents():
            check_fees(tx)
        dump_tx(tx, args.network, args.verbose_signature, args.disassemble,
                args.trace, args.pdb)
        if include_unspents:
            print(
                "including unspents in hex dump since transaction not fully signed"
            )
        print(tx_as_hex)

    if args.cache:
        if tx_db is None:
            warning_tx_cache = message_about_tx_cache_env()
            warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(
                args.network)
            tx_db = get_tx_db(args.network)
        tx_db.put(tx)

    if args.bitcoind_url:
        if tx_db is None:
            warning_tx_cache = message_about_tx_cache_env()
            warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(
                args.network)
            tx_db = get_tx_db(args.network)
        validate_bitcoind(tx, tx_db, args.bitcoind_url)

    if tx.missing_unspents():
        print("\n** can't validate transaction as source transactions missing",
              file=sys.stderr)
    else:
        try:
            if tx_db is None:
                warning_tx_cache = message_about_tx_cache_env()
                warning_tx_for_tx_hash = message_about_tx_for_tx_hash_env(
                    args.network)
                tx_db = get_tx_db(args.network)
            tx.validate_unspents(tx_db)
            print('all incoming transaction values validated')
        except BadSpendableError as ex:
            print("\n**** ERROR: FEES INCORRECTLY STATED: %s" % ex.args[0],
                  file=sys.stderr)
        except Exception as ex:
            print(
                "\n*** can't validate source transactions as untampered: %s" %
                ex.args[0],
                file=sys.stderr)

    # print warnings
    for m in [warning_tx_cache, warning_tx_for_tx_hash, warning_spendables]:
        if m:
            print("warning: %s" % m, file=sys.stderr)
コード例 #4
0
ファイル: txn.py プロジェクト: yahiheb/firmware
    def doit(num_ins,
             num_outs,
             master_xpub=None,
             subpath="0/%d",
             fee=10000,
             outvals=None,
             segwit_in=False,
             outstyles=['p2pkh'],
             psbt_hacker=None,
             change_outputs=[],
             capture_scripts=None,
             add_xpub=None):
        psbt = BasicPSBT()
        txn = Tx(2, [], [])
        master_xpub = master_xpub or simulator_fixed_xprv

        # we have a key; use it to provide "plausible" value inputs
        mk = BIP32Node.from_wallet_key(master_xpub)
        xfp = mk.fingerprint()

        psbt.inputs = [BasicPSBTInput(idx=i) for i in range(num_ins)]
        psbt.outputs = [BasicPSBTOutput(idx=i) for i in range(num_outs)]

        for i in range(num_ins):
            # make a fake txn to supply each of the inputs
            # - each input is 1BTC

            # addr where the fake money will be stored.
            subkey = mk.subkey_for_path(subpath % i)
            sec = subkey.sec()
            assert len(sec) == 33, "expect compressed"
            assert subpath[0:2] == '0/'

            psbt.inputs[i].bip32_paths[sec] = xfp + pack('<II', 0, i)

            # UTXO that provides the funding for to-be-signed txn
            supply = Tx(2, [TxIn(pack('4Q', 0xdead, 0xbeef, 0, 0), 73)], [])

            scr = bytes([0x76, 0xa9, 0x14]) + subkey.hash160() + bytes(
                [0x88, 0xac])

            supply.txs_out.append(TxOut(1E8, scr))

            with BytesIO() as fd:
                if not segwit_in:
                    supply.stream(fd)
                    psbt.inputs[i].utxo = fd.getvalue()
                else:
                    supply.txs_out[-1].stream(fd)
                    psbt.inputs[i].witness_utxo = fd.getvalue()

            spendable = TxIn(supply.hash(), 0)
            txn.txs_in.append(spendable)

        for i in range(num_outs):
            # random P2PKH
            if not outstyles:
                style = ADDR_STYLES[i % len(ADDR_STYLES)]
            else:
                style = outstyles[i % len(outstyles)]

            if i in change_outputs:
                scr, act_scr, isw, pubkey, sp = make_change_addr(mk, style)
                psbt.outputs[i].bip32_paths[pubkey] = sp
            else:
                scr = act_scr = fake_dest_addr(style)
                isw = ('w' in style)

            assert scr
            act_scr = act_scr or scr

            if isw:
                psbt.outputs[i].witness_script = scr
            elif style.endswith('sh'):
                psbt.outputs[i].redeem_script = scr

            if not outvals:
                h = TxOut(round(((1E8 * num_ins) - fee) / num_outs, 4),
                          act_scr)
            else:
                h = TxOut(outvals[i], act_scr)

            if capture_scripts is not None:
                capture_scripts.append(act_scr)

            txn.txs_out.append(h)

        with BytesIO() as b:
            txn.stream(b)
            psbt.txn = b.getvalue()

        if add_xpub:
            # some people want extra xpub data in their PSBTs
            from pycoin.encoding import a2b_base58
            psbt.xpubs = [(a2b_base58(master_xpub), xfp)]

        # last minute chance to mod PSBT object
        if psbt_hacker:
            psbt_hacker(psbt)

        rv = BytesIO()
        psbt.serialize(rv)
        assert rv.tell() <= MAX_TXN_LEN, 'too fat'

        return rv.getvalue()
コード例 #5
0
    def _test_sighash_single(self, network):
        Key = network.ui._key_class
        k0 = Key(secret_exponent=PRIV_KEYS[0],
                 generator=secp256k1_generator,
                 is_compressed=True)
        k1 = Key(secret_exponent=PRIV_KEYS[1],
                 generator=secp256k1_generator,
                 is_compressed=True)
        k2 = Key(secret_exponent=PRIV_KEYS[2],
                 generator=secp256k1_generator,
                 is_compressed=True)
        k3 = Key(secret_exponent=PRIV_KEYS[3],
                 generator=secp256k1_generator,
                 is_compressed=True)
        k4 = Key(secret_exponent=PRIV_KEYS[4],
                 generator=secp256k1_generator,
                 is_compressed=True)
        k5 = Key(secret_exponent=PRIV_KEYS[5],
                 generator=secp256k1_generator,
                 is_compressed=True)

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

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

        script_for_address = network.ui.script_for_address

        # 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, script_for_address(k3.address())),
            TxOut(800000000, script_for_address(k4.address())),
            TxOut(800000000, script_for_address(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

        solution_checker = BitcoinSolutionChecker(tx)
        sig_hash = solution_checker._signature_hash(
            coinbase_tx.txs_out[0].script, 0, sig_type)
        self.assertEqual(
            0xcc52d785a3b4133504d1af9e60cd71ca422609cb41df3a08bbb466b2a98a885e,
            sig_hash)

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

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

        sig_hash = solution_checker._signature_hash(
            coinbase_tx.txs_out[1].script, 1, sig_type)
        self.assertEqual(
            0x93bb883d70fccfba9b8aa2028567aca8357937c65af7f6f5ccc6993fd7735fb7,
            sig_hash)

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

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

        sig_hash = solution_checker._signature_hash(
            coinbase_tx.txs_out[2].script, 2, sig_type)
        self.assertEqual(
            0x53ef7f67c3541bffcf4e0d06c003c6014e2aa1fb38ff33240b3e1c1f3f8e2a35,
            sig_hash)

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

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

        sig_type = SIGHASH_SINGLE | SIGHASH_ANYONECANPAY

        sig_hash = solution_checker._signature_hash(
            coinbase_tx.txs_out[0].script, 0, sig_type)
        self.assertEqual(
            0x2003393d246a7f136692ce7ab819c6eadc54ffea38eb4377ac75d7d461144e75,
            sig_hash)

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

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

        sig_hash = solution_checker._signature_hash(
            coinbase_tx.txs_out[1].script, 1, sig_type)
        self.assertEqual(
            0xe3f469ac88e9f35e8eff0bd8ad4ad3bf899c80eb7645947d60860de4a08a35df,
            sig_hash)

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

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

        sig_hash = solution_checker._signature_hash(
            coinbase_tx.txs_out[2].script, 2, sig_type)
        self.assertEqual(
            0xbacd7c3ab79cad71807312677c1788ad9565bf3c00ab9a153d206494fb8b7e6a,
            sig_hash)

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

        tx.txs_in[2].script = BitcoinScriptTools.compile(b2h(sig))
        self.assertTrue(tx.is_signature_ok(2))
コード例 #6
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