Beispiel #1
0
 def decode_spendables(address, results):
     spendables = [
         Spendable(r['value'], ScriptPayToAddress(address),
                   h2b_rev(r['tx_hash']), r['tx_pos'])
         for r in results['result']
     ]
     return spendables
Beispiel #2
0
def make_bare_tx(candidate, change_address, rs_asm, version=1):

    # <Tx> components
    spendables = []
    ins = []
    outs = []

    # estimate the final (signed) bytesize per input based on the redeemscript
    redeem_script = tools.compile(rs_asm)
    in_size = estimate_input_size(redeem_script)

    # initialize size and amount counters
    in_amount = Decimal(0)
    est_size = TX_COMPONENTS.version + TX_COMPONENTS.out_count + TX_COMPONENTS.in_count

    # add output size
    est_size += OUTSIZE * 2

    # iterate over unspents
    for utxo in candidate.utxos:

        value = Decimal(utxo.amount) * COIN
        in_amount += value

        script = h2b(utxo.script)
        # for now: test if the in_script we figured we would need, actually matches the in script :D

        # reverse that tx hash
        prevtx = h2b_rev(utxo.hash)

        # output index
        outnum = utxo.outpoint

        # create "spendable"
        spdbl = Spendable(value, script, prevtx, outnum)
        spendables.append(spdbl)

        # also create this as input
        as_input = spdbl.tx_in()
        as_input.sigs = []
        ins.append(as_input)

        # add the estimated size per input
        est_size += in_size

    # calc fee and out amount
    fee = (Decimal(math.ceil(est_size / 1000)) * COIN *
           NETWORK_FEE) + FEE_MARGIN
    change_amount = Decimal(
        math.floor(in_amount - (candidate.amount * COIN) - fee))

    # create outputs
    outs.append(
        TxOut(int(candidate.amount * COIN), make_payto(candidate.address)))
    outs.append(TxOut(int(change_amount), make_payto_script(change_address)))

    # create bare tx without sigs
    tx = Tx(version, ins, outs, 0, spendables)

    return tx
 def spendable_for_row(r):
     return Spendable(coin_value=r[2],
                      script=h2b(r[3]),
                      tx_hash=h2b_rev(r[0]),
                      tx_out_index=r[1],
                      block_index_available=r[4],
                      does_seem_spent=r[5],
                      block_index_spent=r[6])
Beispiel #4
0
 def test_create_trx(self):
     tx_input = Spendable(200, '18eKkAWyU9kvRNHPKxnZb6wwtPMrNmRRRA',
                          h2b('8443b07464c762d7fb404ea918a5ac9b3618d5cd6a0c5ea6e4dd5d7bbe28b154'), 0)
     tx_outs = [tx_utils.create_transaction_output('mgAqW5ZCnEp7fjvpj8RUL3WxsBy8rcDcCi', 0.0000275)]
     tx = tx_utils.create_trx('TEST'.encode('utf-8'), 3, 'mgAqW5ZCnEp7fjvpj8RUL3WxsBy8rcDcCi', tx_outs, tx_input)
     hextx = hexlify(tx.serialize())
     self.assertEquals(hextx,
                       '01000000018443b07464c762d7fb404ea918a5ac9b3618d5cd6a0c5ea6e4dd5d7bbe28b1540000000000ffffffff0300000000000000001976a914072a22e5913cd939904c46bbd0bc56755543384b88acc5000000000000001976a914072a22e5913cd939904c46bbd0bc56755543384b88ac0000000000000000066a045445535400000000')
 def spendables_for_addresses(self, addresses):
     results = {}
     for address in addresses:
         if address == "mgMy4vmqChGT8XeKPb1zD6RURWsiNmoNvR":
             results[address] =\
                 [Spendable(coin_value=10000,
                            script=ScriptPayToAddress(bitcoin_address_to_hash160_sec(address, address_prefix_for_netcode('XTN'))).script(),
                            tx_out_index=0, tx_hash=b'2'*32)]
     return results
Beispiel #6
0
def op_return_this(privatekey, text, prefix = "KEYSTAMP:", bitcoin_fee = 10000):

    bitcoin_keyobj = get_key(privatekey)
    bitcoin_address = bitcoin_keyobj.bitcoin_address()

    ## Get the spendable outputs we are going to use to pay the fee
    all_spendables = get_spendables_blockcypher(bitcoin_address)
    spendables = []
    value = 0
    for unspent in all_spendables:
        while value < bitcoin_fee + 10000:
            coin_value = unspent.get("value")
            script = h2b(unspent.get("script_hex"))
            previous_hash = h2b_rev(unspent.get("tx_hash"))
            previous_index = unspent.get("index")
            spendables.append(Spendable(coin_value, script, previous_hash, previous_index))
            value += coin_value

    bitcoin_sum = sum(spendable.coin_value for spendable in spendables)
    if(bitcoin_sum < bitcoin_fee):
        print "ERROR: not enough balance: available: %s - fee: %s" %(bitcoin_sum, bitcoin_fee)
        return False

    ## Create the inputs we are going to use
    inputs = [spendable.tx_in() for spendable in spendables]
    ## If we will have change left over create an output to send it back
    outputs = []
    if (bitcoin_sum > bitcoin_fee):
        change_output_script = standard_tx_out_script(bitcoin_address)
        total_amout = bitcoin_sum - bitcoin_fee
        outputs.append(TxOut(total_amout, change_output_script))

        # home_address = standard_tx_out_script(bitcoin_address)
        # #TODO: it needs some love and IQ on input mananagement stuff
        # outputs.append(TxOut((bitcoin_sum - bitcoin_fee), home_address))

    ## Build the OP_RETURN output with our message
    if prefix is not None and (len(text) + len(prefix) <= 80):
        text = prefix + text

    message = hexlify(text.encode()).decode('utf8')

    op_return_output_script = tools.compile("OP_RETURN %s" % message)
    outputs.append(TxOut(0, op_return_output_script))

    ## Create the transaction and sign it with the private key
    tx = Tx(version=1, txs_in=inputs, txs_out=outputs)
    # print tx.as_hex()
    # print spendables
    tx.set_unspents(spendables)
    sign_tx(tx, wifs=[privatekey])

    print "singed_tx: %s" %tx.as_hex()

    #TODO: uncomment this when its ready to push data to blockchian
    tx_hash = broadcast_tx_blockr(tx.as_hex())
    return tx_hash
Beispiel #7
0
    def test_verify_transaction(self):
        tx_input = Spendable(200, '18eKkAWyU9kvRNHPKxnZb6wwtPMrNmRRRA',
                             h2b('8443b07464c762d7fb404ea918a5ac9b3618d5cd6a0c5ea6e4dd5d7bbe28b154'), 0)
        tx_outs = [tx_utils.create_transaction_output('mgAqW5ZCnEp7fjvpj8RUL3WxsBy8rcDcCi', 0.0000275)]
        op_return_val = h2b('e9cee71ab932fde863338d08be4de9dfe39ea049bdafb342ce659ec5450b69ae')
        tx = tx_utils.create_trx(op_return_val, 3, 'mgAqW5ZCnEp7fjvpj8RUL3WxsBy8rcDcCi', tx_outs, tx_input)

        hextx = hexlify(tx.serialize())

        tx_utils.verify_transaction(hextx, hexlify(op_return_val))
 def spendables_for_address(self, address):
     if address == "1r1msgrPfqCMRAhg23cPBD9ZXH1UQ6jec":
         return [
             Spendable(
                 coin_value=10000,
                 script=ScriptPayToAddress(
                     bitcoin_address_to_hash160_sec(address)).script(),
                 tx_out_index=0,
                 tx_hash=b'2' * 32)
         ]
     else:
         return []
Beispiel #9
0
 def spendables_for_address(self, bitcoin_address):
     URL = "%s/api/addr/%s/utxo" % (self.base_url, bitcoin_address)
     r = json.loads(urlopen(URL).read().decode("utf8"))
     spendables = []
     for u in r:
         value = btc_to_satoshi(str(u.get("amount")))
         script = h2b(u.get("scriptPubKey"))
         prev_hash = h2b_rev(u.get("txid"))
         prev_index = u.get("vout")
         spendable = Spendable(value, script, prev_hash, prev_index)
         spendables.append(spendable)
     return spendables
Beispiel #10
0
 def spendable_for_hash_index(self, tx_hash, tx_out_index):
     tx_hash_hex = b2h_rev(tx_hash)
     SQL = ("select coin_value, script, block_index_available, "
            "does_seem_spent, block_index_spent from Spendable where "
            "tx_hash = ? and tx_out_index = ?")
     c = self._exec_sql(SQL, tx_hash_hex, tx_out_index)
     r = c.fetchone()
     if r is None:
         return r
     return Spendable(coin_value=r[0], script=h2b(r[1]), tx_hash=tx_hash,
                      tx_out_index=tx_out_index, block_index_available=r[2],
                      does_seem_spent=r[3], block_index_spent=r[4])
 def spendables_for_address(self, address):
     if address == "32pQeKJ8KzRfb3ox9Me8EHn3ud8xo6mqAu":
         if address != payto_script.address():
             raise ValueError()
         return [
             Spendable(coin_value=10000,
                       script=payto_script.script(),
                       tx_out_index=0,
                       tx_hash=b'2' * 32)
         ]
     else:
         return []
Beispiel #12
0
 def spendables_for_address(self, bitcoin_address):
     url = "{0}/addr/{1}/utxo".format(self.base_url, bitcoin_address)
     result = json.loads(urlopen(url).read().decode("utf8"))
     spendables = []
     for utxo in result:
         value = btc_to_satoshi(str(utxo["amount"]))
         prev_index = utxo["vout"]
         prev_hash = h2b_rev(utxo["txid"])
         script = h2b(utxo["scriptPubKey"])
         spendable = Spendable(value, script, prev_hash, prev_index)
         spendables.append(spendable)
     return spendables
 def spendables_for_address(self, address):
     if address == "mgMy4vmqChGT8XeKPb1zD6RURWsiNmoNvR":
         script = ScriptPayToAddress(
             bitcoin_address_to_hash160_sec(
                 address, address_prefix_for_netcode('XTN')))
         return [
             Spendable(coin_value=10000,
                       script=script.script(),
                       tx_out_index=0,
                       tx_hash=b'2' * 32)
         ]
     else:
         return []
Beispiel #14
0
 def spendables_for_address(self, bitcoin_address):
     """
     Return a list of Spendable objects for the
     given bitcoin address.
     """
     URL = "https://blockchain.info/unspent?active=%s" % bitcoin_address
     r = json.loads(urlopen(URL).read().decode("utf8"))
     spendables = []
     for u in r["unspent_outputs"]:
         coin_value = u["value"]
         script = h2b(u["script"])
         previous_hash = h2b(u["tx_hash"])
         previous_index = u["tx_output_n"]
         spendables.append(Spendable(coin_value, script, previous_hash, previous_index))
     return spendables
Beispiel #15
0
 def spendables_for_address(self, bitcoin_address):
     """
     Return a list of Spendable objects for the
     given bitcoin address.
     """
     URL = "%s/api/addr/%s/utxo" % (self.base_url, bitcoin_address)
     r = json.loads(urlopen(URL).read().decode("utf8"))
     spendables = []
     for u in r:
         coin_value = btc_to_satoshi(u.get("amount"))
         script = h2b(u.get("scriptPubKey"))
         previous_hash = h2b_rev(u.get("txid"))
         previous_index = u.get("vout")
         spendables.append(Spendable(coin_value, script, previous_hash, previous_index))
     return spendables
Beispiel #16
0
def spendables_for_address(bitcoin_address):
    """
    Return a list of Spendable objects for the
    given bitcoin address.
    """
    URL = "http://btc.blockr.io/api/v1/address/unspent/%s" % bitcoin_address
    r = json.loads(urlopen(URL).read().decode("utf8"))
    spendables = []
    for u in r.get("data", {}).get("unspent", []):
        coin_value = btc_to_satoshi(u.get("amount"))
        script = binascii.unhexlify(u.get("script"))
        previous_hash = h2b_rev(u.get("tx"))
        previous_index = u.get("n")
        spendables.append(
            Spendable(coin_value, script, previous_hash, previous_index))
    return spendables
Beispiel #17
0
 def spendables_for_address(self, address):
     """
     Return a list of Spendable objects for the
     given bitcoin address.
     """
     spendables = []
     url_append = "?unspentOnly=true&token=%s&includeScript=true" % self.api_key
     url = self.base_url("addrs/%s%s" % (address, url_append))
     result = json.loads(urlopen(url).read().decode("utf8"))
     for txn in result.get("txrefs", []):
         coin_value = txn.get("value")
         script = h2b(txn.get("script"))
         previous_hash = h2b_rev(txn.get("tx_hash"))
         previous_index = txn.get("tx_output_n")
         spendables.append(Spendable(coin_value, script, previous_hash, previous_index))
     return spendables
Beispiel #18
0
 def spendables_for_address(self, address):
     """
     Return a list of Spendable objects for the
     given bitcoin address.
     """
     url_append = "unspent/%s" % address
     URL = self.base_url("/address/%s" % url_append)
     r = json.loads(urlopen(URL).read().decode("utf8"))
     spendables = []
     for u in r.get("data", {}).get("unspent", []):
         coin_value = btc_to_satoshi(u.get("amount"))
         script = h2b(u.get("script"))
         previous_hash = h2b_rev(u.get("tx"))
         previous_index = u.get("n")
         spendables.append(Spendable(coin_value, script, previous_hash, previous_index))
     return spendables
Beispiel #19
0
def spendables_for_address(bitcoin_address):
    """
    Return a list of Spendable objects for the
    given bitcoin address.
    """
    json_response = fetch_json("addresses/%s/unspent-outputs" %
                               bitcoin_address)
    spendables = []
    for tx_out_info in json_response.get("data", {}).get("outputs"):
        if tx_out_info.get("to_address") == bitcoin_address:
            coin_value = tx_out_info["value"]
            script = tools.compile(tx_out_info.get("script_pub_key"))
            previous_hash = h2b_rev(tx_out_info.get("transaction_hash"))
            previous_index = tx_out_info.get("transaction_index")
            spendables.append(
                Spendable(coin_value, script, previous_hash, previous_index))
    return spendables
Beispiel #20
0
 def spendables_for_address(self, address):
     """
     Converts to pycoin Spendable type
     :param address:
     :return: list of Spendables
     """
     unspent_outputs = bitcoin.rpc.Proxy().listunspent(addrs=[address])
     spendables = []
     for unspent in unspent_outputs:
         coin_value = unspent.get('amount', 0)
         outpoint = unspent.get('outpoint')
         script = unspent.get('scriptPubKey')
         previous_hash = outpoint.hash
         previous_index = outpoint.n
         spendables.append(
             Spendable(coin_value, script, previous_hash, previous_index))
     return spendables
Beispiel #21
0
def spendables_for_address(
    _from, unused_utxo
):  # Generate spendables without connecting to the Bitcoin network
    spendables = []

    _script_p2pkh = PAYMENT_OBJ[_from][1]
    coin_value = unused_utxo.satoshis
    script = binascii.unhexlify(_script_p2pkh)

    previous_hash_big_endian = binascii.unhexlify(unused_utxo.txid)
    previous_hash_little_endian = previous_hash_big_endian[::-1]

    previous_index = unused_utxo.vout

    return [
        Spendable(coin_value, script, previous_hash_little_endian,
                  previous_index)
    ]
Beispiel #22
0
    def unspents_for_addresses(self, address_iter):
        """
        Return a list of Spendable objects for the
        given bitcoin address.
        """
        address_list = ",".join(address_iter)
        URL = self.base_url() % ("addresses/%s/unspents" % address_list)
        r = json.loads(urlopen(URL).read().decode("utf8"))

        spendables = []
        for u in r:
            coin_value = u["value"]
            script = h2b(u["script_hex"])
            previous_hash = h2b(u["transaction_hash"])
            previous_index = u["output_index"]
            spendables.append(
                Spendable(coin_value, script, previous_hash, previous_index))
        return spendables
Beispiel #23
0
    def spendables_for_address(self, address):
        """
        Return a list of Spendable objects for the
        given bitcoin address.
        """
        spendables = []
        r = json.loads(
            urlopen(self.base_url('get_tx_unspent',
                                  address)).read().decode("utf8"))

        for u in r['data']['txs']:
            coin_value = int(float(u['value']) * 100000000)
            script = h2b(u["script_hex"])
            previous_hash = h2b_rev(u["txid"])
            previous_index = u["output_no"]
            spendables.append(
                Spendable(coin_value, script, previous_hash, previous_index))

        return spendables
Beispiel #24
0
 def spendables_for_addresses(self, bitcoin_addresses):
     """
     Return a list of Spendable objects for the
     given bitcoin address.
     """
     r = []
     for i in xrange(0, len(bitcoin_addresses), CHUNK_SIZE):
         addresses = bitcoin_addresses[i:i + CHUNK_SIZE]
         url = "%s/api/addrs/%s/utxo" % (self.base_url, ",".join(addresses))
         r.extend(json.loads(urlopen(url).read().decode("utf8")))
     spendables = []
     for u in r:
         coin_value = btc_to_satoshi(str(u.get("amount")))
         script = h2b(u.get("scriptPubKey"))
         previous_hash = h2b_rev(u.get("txid"))
         previous_index = u.get("vout")
         spendables.append(
             Spendable(coin_value, script, previous_hash, previous_index))
     return spendables
Beispiel #25
0
def spendables_for_address(bitcoin_address):
    """
    Return a list of Spendable objects for the
    given bitcoin address.
    """
    URL = "https://api.biteasy.com/blockchain/v1/addresses/%s/unspent-outputs" % bitcoin_address
    r = Request(URL,
                headers={"content-type": "application/json", "accept": "*/*", "User-Agent": "curl/7.29.0"})
    d = urlopen(r).read()
    json_response = json.loads(d.decode("utf8"))
    spendables = []
    for tx_out_info in json_response.get("data", {}).get("outputs"):
        if tx_out_info.get("to_address") == bitcoin_address:
            coin_value = tx_out_info["value"]
            script = tools.compile(tx_out_info.get("script_pub_key"))
            previous_hash = h2b_rev(tx_out_info.get("transaction_hash"))
            previous_index = tx_out_info.get("transaction_index")
            spendables.append(Spendable(coin_value, script, previous_hash, previous_index))
    return spendables
Beispiel #26
0
    def fake_sources_for_address(self, addr, num_sources, total_satoshi):
        """
        Returns a fake list of funding sources for a bitcoin address.
        
        Note: total_satoshi will be split evenly by num_sources
        
        addr          - bitcoin address to fund
        num_sources   - number of sources to fund it with
        total_satoshi - total satoshis to fund 'addr' with 
    
        Returns a list of Spendable objects
        """

        spendables = []
        satoshi_left = total_satoshi
        satoshi_per_tx = satoshi_left / num_sources
        satoshi_per_tx = int(satoshi_per_tx)

        # Create the output script for the input to fund
        script = standard_tx_out_script(addr)

        while satoshi_left > 0:
            if satoshi_left < satoshi_per_tx:
                satoshi_per_tx = satoshi_left

            # Create a random hash value
            rand_hash = bytes([random.randint(0, 0xFF) for _ in range(0, 32)])

            # Create a random output index
            # This field is 32 bits, but typically transactions dont have that many, limit to 0xFF
            rand_output_index = random.randint(0, 0xFF)

            # Append the new fake source
            spend = Spendable(satoshi_per_tx, script, rand_hash,
                              rand_output_index)

            spendables.append(spend)

            satoshi_left -= satoshi_per_tx

        assert satoshi_left == 0, "incorrect funding"

        return spendables
Beispiel #27
0
def make_bare_tx(network, from_address, to_address, redeem_script, version=1):

    # <Tx> components
    spendables = []
    ins = []
    outs = []

    # estimate the final (signed) bytesize per input based on the redeemscript
    in_size = estimate_input_size(redeem_script)

    # initialize size and amount counters
    in_amount = Decimal(0)
    est_size = TX_COMPONENTS.version + TX_COMPONENTS.out_count + TX_COMPONENTS.in_count

    # add output size (we"ll only have 1)
    est_size += TX_COMPONENTS.out_scriptlen + TX_COMPONENTS.out_scriptsize + TX_COMPONENTS.out_scriptlen

    unspent_response = sochain_get_unspents(network, from_address)

    unspents = unspent_response.get("txs", [])

    # iterate over unspents
    for tx in unspents:

        value = Decimal(tx.get("value")) * Decimal(1e8)
        in_amount += value

        script = h2b(tx.get("script_hex"))
        # for now: test if the in_script we figured we would need, actually matches the in script :D

        # reverse that tx hash
        txhex = tx.get("txid")
        prevtx = h2b_rev(txhex)

        # output index
        outnum = tx.get("output_no")

        # create "spendable"
        spdbl = Spendable(value, script, prevtx, outnum)
        spendables.append(spdbl)

        # also create this as input
        as_input = spdbl.tx_in()
        as_input.sigs = []
        ins.append(as_input)

        # add the estimated size per input
        est_size += in_size

    # calc fee and out amount
    fee = Decimal(math.ceil(
        est_size / 1000.0)) * Decimal(1e8) * NETWORK_FEES.get(network)
    out_amount = in_amount - fee

    if (is_p2sh(to_address)):
        outscript = make_payto_script(to_address)
    else:
        outscript = make_payto_address(to_address)

    # create output
    outs.append(TxOut(out_amount, outscript))

    # create bare tx without sigs
    tx = Tx(version, ins, outs, 0, spendables)

    return tx
Beispiel #28
0
def create_spend(utxo):
    sp = Spendable(coin_value=utxo['amount'] * 10**6,
                   script=unhexlify(utxo['scriptPubKey']),
                   tx_hash=unhexlify(utxo['txid'])[::-1],
                   tx_out_index=utxo['vout'])
    return sp
Beispiel #29
0
    def test_sign(self, keynums_satoshi, out_addr, out_satoshi, change_keynum,
                  change_satoshi, prevtx_keynums, prevtx_outputs,
                  prevtx_inputs):
        """
        Performs a tx signing test, comparing Polly's signed tx against the reference wallet.
    
        Basic tx signing parameters:
        
        keynums_satoshi - list of tuples (keynum, satoshis) with key indices and their unspent value to 
                          use as tx inputs. Funding above out_satoshi + change_satoshi will be fees.
        out_addr        - output address in bitcoin address format. 
        out_satoshi     - output amount in satoshis.
        change_keynum   - change key index in the wallet, use None for no change.
        change_satoshi  - change amount in satoshis, use 0 for no change. 
        
        
        Supporting (previous) txs will be created to fund keynums and are controlled by these parameters:
        
        prevtx_keynums  - keynums will show up as outputs of previous txs. A number randomly picked 
                          from this list controls how many keynums are chosen to include per prev tx.
        prevtx_outputs  - in addition to previous tx outputs funding keynums, other outputs may 
                          be present. A number randomly picked from this list controls how many 
                          ignored outputs are injected per keynum. 
        prevtx_inputs   - previous txs need inputs too. A number randomly picked from this list 
                          controls how many inputs are chosen per previous tx.
        """

        total_in_satoshi = sum(satoshi for _, satoshi in keynums_satoshi)
        fee_satoshi = total_in_satoshi - out_satoshi - change_satoshi
        chain0 = self.wallet.subkey(0, is_hardened=True).subkey(0)
        chain1 = self.wallet.subkey(0, is_hardened=True).subkey(1)

        assert total_in_satoshi >= out_satoshi + change_satoshi
        assert len(keynums_satoshi) <= 32

        #
        # Step 1: send the inputs and outputs to use in the signed tx
        #

        # Create the (key num, compressed public key) tuple, input keys assume an m/0h/0/keynum path for now.
        keys = [
            (keynum,
             encoding.public_pair_to_sec(chain0.subkey(keynum).public_pair))
            for (keynum, _) in keynums_satoshi
        ]

        # Convert base58 address to raw hex address
        out_addr_160 = encoding.bitcoin_address_to_hash160_sec(out_addr)

        print()
        print("Sign tx parameters:", "")
        for i, (keynum, satoshi) in enumerate(keynums_satoshi):
            print("{:<10}{:16.8f} btc < key {}".format(
                " inputs" if 0 == i else "", satoshi / 100000000, keynum))
        print("{:<10}{:16.8f} btc > {}".format(" output",
                                               out_satoshi / 100000000,
                                               self.hexstr(out_addr_160)))
        print("{:<10}{:16.8f} btc > key {}".format(" change",
                                                   change_satoshi / 100000000,
                                                   change_keynum))
        print("{:<10}{:16.8f} btc".format(" fee", fee_satoshi / 100000000))
        print("{:<10}{:16.8f} btc".format(" total",
                                          total_in_satoshi / 100000000))

        print()
        print(self.PAD.format("Send tx parameters"), end='')

        # ---> send to Polly
        self.polly.send_sign_tx(keys, out_addr_160, out_satoshi, change_keynum,
                                change_satoshi)

        print(self.__outok())

        #
        # Step 2: send previous txs to fund the inputs
        #

        print()

        cur = 0
        prevtx_info = []

        while cur < len(keynums_satoshi):

            prevtx_outputs_satoshi = []

            # Calculate how many keynums will be associated with this prev tx
            end = min(cur + random.choice(prevtx_keynums),
                      len(keynums_satoshi))

            # Create the prev tx output list
            for keynum, satoshi in keynums_satoshi[cur:end]:

                # Inject a random number of outputs not associated with tx input keynums
                for _ in range(0, random.choice(prevtx_outputs)):
                    prevtx_outputs_satoshi.append(
                        (random.randint(0, 0x7FFFFFFF),
                         random.randint(0, 2099999997690000)))

                # Add the outputs funding the tx input keynums
                prevtx_outputs_satoshi.append((keynum, satoshi))

                # Create output script
                addr = chain0.subkey(keynum, as_private=True).bitcoin_address()
                script = standard_tx_out_script(addr)

                # Capture some info we'll use later to verify the signed tx
                prevtx_info.append((
                    keynum,
                    satoshi,
                    script,
                    0,  # This is the hash and will be replaced later
                    len(prevtx_outputs_satoshi) -
                    1))  # Index of the valid output

            print("{:30}{}".format(
                "Make prev tx for keys", " ".join(
                    str(keynum)
                    for (keynum, _, _, _, _) in prevtx_info[cur:])))

            # Create the prev tx
            prevtx = self.create_prev_tx(
                win=Wallet.from_master_secret(
                    bytes(0)),  # create a dummy wallet 
                in_keynum=list(range(0, random.choice(prevtx_inputs))),
                sources_per_input=1,
                wout=chain0,
                out_keynum_satoshi=prevtx_outputs_satoshi,
                fees_satoshi=random.randint(100, 1000))

            # We have built the prev tx, calculate its hash (and reverse the bytes)
            prevtx_hash = encoding.double_sha256(prevtx)[::-1]

            # Update the hashes now that we have a full prev tx
            for i, (keynum, satoshi, script, _,
                    outidx) in enumerate(prevtx_info[cur:]):
                prevtx_info[i + cur] = (keynum, satoshi, script, prevtx_hash,
                                        outidx)

            # Create the index table that matches a keynum index with an ouput index in this prev tx
            idx_table = [
                (keynum_idx + cur, outidx)
                for keynum_idx, (_, _, _, _,
                                 outidx) in enumerate(prevtx_info[cur:])
            ]

            print(self.PAD.format("Send prev tx "), end='')

            # ---> send to Polly
            self.polly.send_prev_tx(idx_table, prevtx)

            print(self.__outok())

            cur = end

        #
        # Step 3: generate a signed tx with the reference wallet and compare against Polly's
        #

        spendables = []
        wifs = []

        # Make sure that the inputs add up correctly, and prep the input_sources for reference wallet signing
        for (keynum, satoshi, script, prevtx_hash, outidx) in prevtx_info:
            spendables.append(Spendable(satoshi, script, prevtx_hash, outidx))
            wifs.append(chain0.subkey(keynum, as_private=True).wif())

        change_addr = chain1.subkey(change_keynum).bitcoin_address()

        payables = [(out_addr, out_satoshi), (change_addr, change_satoshi)]

        print()
        print(self.PAD.format("Make reference signature"))

        signed_tx = create_signed_tx(spendables, payables, wifs, fee_satoshi)
        signed_tx = self.get_tx_bytes(signed_tx)

        print(self.PAD.format("Get signed tx"), end='', flush=True)

        # <--- get the signed tx from Polly
        polly_signed_tx = self.polly.send_get_signed_tx()

        #print(self.txstr(polly_signed_tx))
        #print(self.txstr(signed_tx))

        print(self.__outok())

        # Compare reference wallet signed tx with polly's
        assert signed_tx == polly_signed_tx, "test_sign: signature mismatch\nExpected:\n" + self.hexstr(
            signed_tx) + "\n\nActual:\n" + self.hexstr(polly_signed_tx)