Пример #1
0
def make_tx_add_notify():
    wallet_dict = make_wallets(1, [[1, 0, 0, 0, 0]], mean_amt=4, sdev_amt=0)[0]
    amount = 250000000
    txfee = 10000
    wallet = wallet_dict['wallet']
    sync_wallet(wallet)
    inputs = wallet.select_utxos(0, amount)
    ins = inputs.keys()
    input_value = sum([i['value'] for i in inputs.values()])
    output_addr = wallet.get_new_addr(1, 0)
    change_addr = wallet.get_new_addr(0, 1)
    outs = [{
        'value': amount,
        'address': output_addr
    }, {
        'value': input_value - amount - txfee,
        'address': change_addr
    }]
    tx = bitcoin.mktx(ins, outs)
    de_tx = bitcoin.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        addr = inputs[utxo]['address']
        priv = wallet.get_key_from_addr(addr)
        tx = bitcoin.sign(tx, index, priv)

    unconfirm_called[0] = confirm_called[0] = False
    timeout_unconfirm_called[0] = timeout_confirm_called[0] = False
    jm_single().bc_interface.add_tx_notify(bitcoin.deserialize(tx),
                                           unconfirm_callback,
                                           confirm_callback, output_addr,
                                           timeout_callback)
    return tx
Пример #2
0
    def on_tx_received(self, nick, txhex, offerinfo):
        """Called when the counterparty has sent an unsigned
        transaction. Sigs are created and returned if and only
        if the transaction passes verification checks (see
        verify_unsigned_tx()).
        """
        try:
            tx = btc.deserialize(txhex)
        except (IndexError, SerializationError, SerializationTruncationError) as e:
            return (False, 'malformed txhex. ' + repr(e))
        jlog.info('obtained tx\n' + pprint.pformat(tx))
        goodtx, errmsg = self.verify_unsigned_tx(tx, offerinfo)
        if not goodtx:
            jlog.info('not a good tx, reason=' + errmsg)
            return (False, errmsg)
        jlog.info('goodtx')
        sigs = []
        utxos = offerinfo["utxos"]

        our_inputs = {}
        for index, ins in enumerate(tx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
            if utxo not in utxos:
                continue
            script = self.wallet_service.addr_to_script(utxos[utxo]['address'])
            amount = utxos[utxo]['value']
            our_inputs[index] = (script, amount)

        txs = self.wallet_service.sign_tx(btc.deserialize(unhexlify(txhex)), our_inputs)
        for index in our_inputs:
            sigmsg = unhexlify(txs['ins'][index]['script'])
            if 'txinwitness' in txs['ins'][index]:
                # Note that this flag only implies that the transaction
                # *as a whole* is using segwit serialization; it doesn't
                # imply that this specific input is segwit type (to be
                # fully general, we allow that even our own wallet's
                # inputs might be of mixed type). So, we catch the EngineError
                # which is thrown by non-segwit types. This way the sigmsg
                # will only contain the scriptSig field if the wallet object
                # decides it's necessary/appropriate for this specific input
                # If it is segwit, we prepend the witness data since we want
                # (sig, pub, witnessprogram=scriptSig - note we could, better,
                # pass scriptCode here, but that is not backwards compatible,
                # as the taker uses this third field and inserts it into the
                # transaction scriptSig), else (non-sw) the !sig message remains
                # unchanged as (sig, pub).
                try:
                    scriptSig = btc.pubkey_to_p2wpkh_script(txs['ins'][index]['txinwitness'][1])
                    sigmsg = b''.join(btc.serialize_script_unit(
                x) for x in txs['ins'][index]['txinwitness'] + [scriptSig])
                except IndexError:
                    #the sigmsg was already set before the segwit check
                    pass
            sigs.append(base64.b64encode(sigmsg).decode('ascii'))
        return (True, sigs)
Пример #3
0
def test_verify_tx_input(setup_tx_creation, signall, mktxlist):
    priv = "aa" * 32 + "01"
    addr = bitcoin.privkey_to_address(priv, magicbyte=get_p2pk_vbyte())
    wallet = make_wallets(1, [[2, 0, 0, 0, 0]], 1)[0]['wallet']
    sync_wallet(wallet)
    insfull = wallet.select_utxos(0, 110000000)
    print(insfull)
    if not mktxlist:
        outs = [{"address": addr, "value": 1000000}]
        ins = insfull.keys()
        tx = bitcoin.mktx(ins, outs)
    else:
        out1 = addr + ":1000000"
        ins0, ins1 = insfull.keys()
        print("INS0 is: " + str(ins0))
        print("INS1 is: " + str(ins1))
        tx = bitcoin.mktx(ins0, ins1, out1)
    desertx = bitcoin.deserialize(tx)
    print(desertx)
    if signall:
        privdict = {}
        for index, ins in enumerate(desertx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            ad = insfull[utxo]['address']
            priv = wallet.get_key_from_addr(ad)
            privdict[utxo] = priv
        tx = bitcoin.signall(tx, privdict)
    else:
        for index, ins in enumerate(desertx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            ad = insfull[utxo]['address']
            priv = wallet.get_key_from_addr(ad)
            if index % 2:
                tx = binascii.unhexlify(tx)
            tx = bitcoin.sign(tx, index, priv)
            if index % 2:
                tx = binascii.hexlify(tx)
    desertx2 = bitcoin.deserialize(tx)
    print(desertx2)
    sig, pub = bitcoin.deserialize_script(desertx2['ins'][0]['script'])
    print(sig, pub)
    pubscript = bitcoin.address_to_script(
        bitcoin.pubkey_to_address(pub, magicbyte=get_p2pk_vbyte()))
    sig = binascii.unhexlify(sig)
    pub = binascii.unhexlify(pub)
    sig_good = bitcoin.verify_tx_input(tx, 0, pubscript, sig, pub)
    assert sig_good
Пример #4
0
def cli_broadcast(wallet_name, partial_tx_hex):
    """Given a partially signed transaction retrieved by running
    this script with the -r flag, and assuming that the utxo with
    which the transaction was made is in a Joinmarket wallet, this
    function will complete the signing and then broadcast the transaction.
    This function is useful if the *receiver*'s wallet is Joinmarket; if
    it is Core then the workflow is just `signrawtransaction` then
    `sendrawtransaction`; should be similar for Electrum although haven't tried.
    """
    wallet = cli_get_wallet(wallet_name)
    tx = btc.deserialize(partial_tx_hex)
    num_sigs = 0
    for index, ins in enumerate(tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        #is the utxo in our utxos?
        in_wallet_utxos = wallet.get_utxos_by_mixdepth(False)
        for m, um in in_wallet_utxos.iteritems():
            for k, v in um.iteritems():
                if k == utxo:
                    print("Found utxo in mixdepth: ", m)
                    if isinstance(wallet, SegwitWallet):
                        amount = v['value']
                    else:
                        amount = None
                    signed_tx = btc.sign(partial_tx_hex,
                                         index,
                                         wallet.get_key_from_addr(
                                             v['address']),
                                         amount=amount)
                    num_sigs += 1
    if num_sigs != 1:
        print("Something wrong, expected to get 1 sig, got: ", num_sigs)
        return
    #should be fully signed; broadcast?
    print("Signed tx in hex:")
    print(signed_tx)
    print("In decoded form:")
    print(pformat(btc.deserialize(signed_tx)))
    if not raw_input("Broadcast to network? (y/n): ") == "y":
        print("You chose not to broadcast, quitting.")
        return
    txid = btc.txhash(signed_tx)
    print('txid = ' + txid)
    pushed = jm_single().bc_interface.pushtx(signed_tx)
    if not pushed:
        print("Broadcast failed.")
    else:
        print("Broadcast was successful.")
Пример #5
0
def test_remove_old_utxos(setup_wallet):
    jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
    wallet = get_populated_wallet()

    # add some more utxos to mixdepth 1
    for i in range(3):
        txin = jm_single().bc_interface.grab_coins(wallet.get_internal_addr(1),
                                                   1)
        wallet.add_utxo(unhexlify(txin), 0, wallet.get_script(1, 1, i), 10**8)

    inputs = wallet.select_utxos_(0, 10**8)
    inputs.update(wallet.select_utxos_(1, 2 * 10**8))
    assert len(inputs) == 3

    tx_inputs = list(inputs.keys())
    tx_inputs.append((b'\x12' * 32, 6))

    tx = btc.deserialize(
        btc.mktx([
            '{}:{}'.format(hexlify(txid).decode('ascii'), i)
            for txid, i in tx_inputs
        ], ['0' * 36 + ':' + str(3 * 10**8 - 1000)]))
    binarize_tx(tx)

    removed = wallet.remove_old_utxos_(tx)
    assert len(removed) == len(inputs)

    for txid in removed:
        assert txid in inputs

    balances = wallet.get_balance_by_mixdepth()
    assert balances[0] == 2 * 10**8
    assert balances[1] == 10**8
    assert balances[2] == 0
    assert len(balances) == wallet.max_mixdepth + 1
def make_sign_and_push(ins_full,
                       wallet,
                       amount,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL,
                       estimate_fee = False):
    """Utility function for easily building transactions
    from wallets
    """
    total = sum(x['value'] for x in ins_full.values())
    ins = list(ins_full.keys())
    #random output address and change addr
    output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr
    change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr
    fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000
    outs = [{'value': amount,
             'address': output_addr}, {'value': total - amount - fee_est,
                                       'address': change_addr}]

    de_tx = btc.deserialize(btc.mktx(ins, outs))
    scripts = {}
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        script = wallet.addr_to_script(ins_full[utxo]['address'])
        scripts[index] = (script, ins_full[utxo]['value'])
    binarize_tx(de_tx)
    de_tx = wallet.sign_tx(de_tx, scripts, hashcode=hashcode)
    #pushtx returns False on any error
    tx = binascii.hexlify(btc.serialize(de_tx)).decode('ascii')
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        return btc.txhash(tx)
    else:
        return False
Пример #7
0
    def on_JM_TX_RECEIVED(self, nick, txhex, offer):
        # "none" flags p2ep protocol; pass through to the generic
        # on_tx handler for that:
        if offer == "none":
            return self.on_p2ep_tx_received(nick, txhex)

        offer = json.loads(offer)
        retval = self.client.on_tx_received(nick, txhex, offer)
        if not retval[0]:
            jlog.info("Maker refuses to continue on receipt of tx")
        else:
            sigs = retval[1]
            self.finalized_offers[nick] = offer
            tx = btc.deserialize(txhex)
            self.finalized_offers[nick]["txd"] = tx
            jm_single().bc_interface.add_tx_notify(
                tx,
                self.unconfirm_callback,
                self.confirm_callback,
                offer["cjaddr"],
                wallet_name=jm_single().bc_interface.get_wallet_name(
                    self.client.wallet),
                txid_flag=False,
                vb=get_p2sh_vbyte())
            d = self.callRemote(commands.JMTXSigs,
                                nick=nick,
                                sigs=json.dumps(sigs))
            self.defaultCallbacks(d)
        return {"accepted": True}
Пример #8
0
def create_tx_and_offerlist(cj_addr,
                            cj_change_addr,
                            other_output_scripts,
                            cj_script=None,
                            cj_change_script=None,
                            offertype='swreloffer'):
    assert len(other_output_scripts) % 2 == 0, "bug in test"

    cj_value = 100000000
    maker_total_value = cj_value * 3

    if cj_script is None:
        cj_script = btc.address_to_script(cj_addr)
    if cj_change_script is None:
        cj_change_script = btc.address_to_script(cj_change_addr)

    inputs = create_tx_inputs(3)
    outputs = create_tx_outputs(
        (cj_script, cj_value),
        (cj_change_script, maker_total_value - cj_value),  # cjfee=0, txfee=0
        *((script, cj_value + (i%2)*(50000000+i)) \
            for i, script in enumerate(other_output_scripts))
    )

    maker_utxos = [inputs[0]]

    tx = btc.deserialize(btc.mktx(inputs, outputs))
    offerlist = construct_tx_offerlist(cj_addr, cj_change_addr, maker_utxos,
                                       maker_total_value, cj_value, offertype)

    return tx, offerlist
def get_tx_info(txid):
    """
    Retrieve some basic information about the given transaction.

    :param txid: txid as hex-str
    :return: tuple
        is_coinjoin: bool
        cj_amount: int, only useful if is_coinjoin==True
        cj_n: int, number of cj participants, only useful if is_coinjoin==True
        output_script_values: {script: value} dict including all outputs
        blocktime: int, blocktime this tx was mined
        txd: deserialized transaction object (hex-encoded data)
    """
    rpctx = jm_single().bc_interface.rpc('gettransaction', [txid])
    txhex = str(rpctx['hex'])
    txd = btc.deserialize(txhex)
    output_script_values = {
        binascii.unhexlify(sv['script']): sv['value']
        for sv in txd['outs']
    }
    value_freq_list = sorted(Counter(
        output_script_values.values()).most_common(),
                             key=lambda x: -x[1])
    non_cj_freq = (0 if len(value_freq_list) == 1 else sum(
        next(islice(zip(*value_freq_list[1:]), 1, None))))
    is_coinjoin = (value_freq_list[0][1] > 1
                   and value_freq_list[0][1] in [non_cj_freq, non_cj_freq + 1])
    cj_amount = value_freq_list[0][0]
    cj_n = value_freq_list[0][1]
    return is_coinjoin, cj_amount, cj_n, output_script_values,\
        rpctx.get('blocktime', 0), txd
Пример #10
0
def fund_wallet_addr(wallet, addr, value_btc=1):
    txin_id = jm_single().bc_interface.grab_coins(addr, value_btc)
    txinfo = jm_single().bc_interface.rpc('gettransaction', [txin_id])
    txin = btc.deserialize(unhexlify(txinfo['hex']))
    utxo_in = wallet.add_new_utxos_(txin, unhexlify(txin_id))
    assert len(utxo_in) == 1
    return list(utxo_in.keys())[0]
Пример #11
0
def cli_receive(filename):
    wif_privkey = raw_input("Enter private key in WIF compressed format: ")
    try:
        privkey = btc.from_wif_privkey(wif_privkey, vbyte=get_p2pk_vbyte())
    except:
        print("Could not parse WIF privkey, quitting.")
        return
    amount = raw_input("Enter amount of utxo being spent, in satoshis: ")
    valid_coinjoins = scan_for_coinjoins(privkey, int(amount), filename)
    if not valid_coinjoins:
        print("Found no valid coinjoins")
        return
    for vc in valid_coinjoins:
        addr, priv, tx = vc
        print("Found signable coinjoin with destination address: ", addr)
        #TODO find a more sensible file naming
        fn = btc.txhash(tx) + ".txt"
        with open(fn, "wb") as f:
            f.write("SNICKER output file for receiver\n"
                    "================================\n")
            f.write("The serialized transaction in hex:\n")
            f.write(tx + "\n")
            f.write("YOUR DESTINATION: " + addr + "\n")
            f.write("PRIVATE KEY FOR THIS DESTINATION ADDRESS:\n")
            f.write(
                btc.wif_compressed_privkey(priv, vbyte=get_p2pk_vbyte()) +
                "\n")
            f.write("The decoded transaction:\n")
            f.write(pformat(btc.deserialize(tx)) + "\n")
        print("The partially signed transaction and the private key for your "
              "output are stored in the file: " + fn)
        print(
            "Pass the transaction hex to `signrawtransaction` in Bitcoin Core "
            "or similar if you wish to broadcast the transaction.")
Пример #12
0
def test_add_new_utxos(setup_wallet):
    jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
    wallet = get_populated_wallet(num=1)

    scripts = [wallet.get_new_script(x, True) for x in range(3)]
    tx_scripts = list(scripts)
    tx_scripts.append(b'\x22' * 17)

    tx = btc.deserialize(
        btc.mktx(['0' * 64 + ':2'], [{
            'script': hexlify(s).decode('ascii'),
            'value': 10**8
        } for s in tx_scripts]))
    binarize_tx(tx)
    txid = b'\x01' * 32
    added = wallet.add_new_utxos_(tx, txid)
    assert len(added) == len(scripts)

    added_scripts = {x['script'] for x in added.values()}
    for s in scripts:
        assert s in added_scripts

    balances = wallet.get_balance_by_mixdepth()
    assert balances[0] == 2 * 10**8
    assert balances[1] == 10**8
    assert balances[2] == 10**8
    assert len(balances) == wallet.max_mixdepth + 1
def test_coinjoin_mixdepth_wrap_maker(monkeypatch, tmpdir, setup_cj):
    def raise_exit(i):
        raise Exception("sys.exit called")

    monkeypatch.setattr(sys, 'exit', raise_exit)
    set_commitment_file(str(tmpdir.join('commitments.json')))

    MAKER_NUM = 2
    wallet_services = make_wallets_to_list(
        make_wallets(MAKER_NUM + 1,
                     wallet_structures=[[0, 0, 0, 0, 4]] * MAKER_NUM +
                     [[3, 0, 0, 0, 0]],
                     mean_amt=1))

    for wallet_service in wallet_services:
        assert wallet_service.max_mixdepth == 4

    jm_single().bc_interface.tickchain()
    jm_single().bc_interface.tickchain()

    sync_wallets(wallet_services)

    cj_fee = 2000
    makers = [
        YieldGeneratorBasic(wallet_services[i],
                            [0, cj_fee, 0, 'swabsoffer', 10**7])
        for i in range(MAKER_NUM)
    ]

    orderbook = create_orderbook(makers)
    assert len(orderbook) == MAKER_NUM

    cj_amount = int(1.1 * 10**8)
    # mixdepth, amount, counterparties, dest_addr, waittime, rounding
    schedule = [(0, cj_amount, MAKER_NUM, 'INTERNAL', 0, NO_ROUNDING)]
    taker = create_taker(wallet_services[-1], schedule, monkeypatch)

    active_orders, maker_data = init_coinjoin(taker, makers, orderbook,
                                              cj_amount)

    txdata = taker.receive_utxos(maker_data)
    assert txdata[0], "taker.receive_utxos error"

    taker_final_result = do_tx_signing(taker, makers, active_orders, txdata)
    assert taker_final_result is not False

    tx = btc.deserialize(txdata[2])
    binarize_tx(tx)

    for i in range(MAKER_NUM):
        wallet_service = wallet_services[i]
        # TODO as above re: monitoring
        wallet_service.remove_old_utxos_(tx)
        wallet_service.add_new_utxos_(tx, b'\x00' * 32)  # fake txid

        balances = wallet_service.get_balance_by_mixdepth()
        assert balances[0] == cj_amount
        assert balances[4] == 4 * 10**8 - cj_amount + cj_fee
Пример #14
0
def make_sign_and_push(ins_full,
                       wallet_service,
                       amount,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL,
                       estimate_fee=False):
    """Utility function for easily building transactions
    from wallets.
    """
    assert isinstance(wallet_service, WalletService)
    total = sum(x['value'] for x in ins_full.values())
    ins = ins_full.keys()
    #random output address and change addr
    output_addr = wallet_service.get_new_addr(
        1,
        BaseWallet.ADDRESS_TYPE_INTERNAL) if not output_addr else output_addr
    change_addr = wallet_service.get_new_addr(
        0,
        BaseWallet.ADDRESS_TYPE_INTERNAL) if not change_addr else change_addr
    fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000
    outs = [{
        'value': amount,
        'address': output_addr
    }, {
        'value': total - amount - fee_est,
        'address': change_addr
    }]

    tx = btc.mktx(ins, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        addr = ins_full[utxo]['address']
        priv = wallet_service.get_key_from_addr(addr)
        if index % 2:
            priv = binascii.unhexlify(priv)
        tx = btc.sign(tx, index, priv, hashcode=hashcode)
    #pushtx returns False on any error
    print(btc.deserialize(tx))
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        return btc.txhash(tx)
    else:
        return False
Пример #15
0
def sign_tx(wallet_service, tx, utxos):
    stx = deserialize(tx)
    our_inputs = {}
    for index, ins in enumerate(stx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        script = wallet_service.addr_to_script(utxos[utxo]['address'])
        amount = utxos[utxo]['value']
        our_inputs[index] = (script, amount)
    return wallet_service.sign_tx(stx, our_inputs)
Пример #16
0
def test_segwit_valid_txs(setup_segwit):
    with open("test/tx_segwit_valid.json", "r") as f:
        json_data = f.read()
    valid_txs = json.loads(json_data)
    for j in valid_txs:
        if len(j) < 2:
            continue
        deserialized_tx = btc.deserialize(str(j[1]))
        print pformat(deserialized_tx)
        assert btc.serialize(deserialized_tx) == str(j[1])
 def get_deser_from_gettransaction(self, rpcretval):
     """Get full transaction deserialization from a call
     to `gettransaction`
     """
     if not "hex" in rpcretval:
         log.info("Malformed gettransaction output")
         return None
     #str cast for unicode
     hexval = str(rpcretval["hex"])
     return btc.deserialize(hexval)
Пример #18
0
def test_spend_p2wpkh(setup_tx_creation):
    #make 3 p2wpkh outputs from 3 privs
    privs = [struct.pack(b'B', x) * 32 + b'\x01' for x in range(1, 4)]
    pubs = [
        bitcoin.privkey_to_pubkey(binascii.hexlify(priv).decode('ascii'))
        for priv in privs
    ]
    scriptPubKeys = [bitcoin.pubkey_to_p2wpkh_script(pub) for pub in pubs]
    addresses = [bitcoin.pubkey_to_p2wpkh_address(pub) for pub in pubs]
    #pay into it
    wallet_service = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet']
    wallet_service.sync_wallet(fast=True)
    amount = 35000000
    p2wpkh_ins = []
    for addr in addresses:
        ins_full = wallet_service.select_utxos(0, amount)
        txid = make_sign_and_push(ins_full,
                                  wallet_service,
                                  amount,
                                  output_addr=addr)
        assert txid
        p2wpkh_ins.append(txid + ":0")
        #wait for mining
        time.sleep(1)
    #random output address
    output_addr = wallet_service.get_internal_addr(1)
    amount2 = amount * 3 - 50000
    outs = [{'value': amount2, 'address': output_addr}]
    tx = bitcoin.mktx(p2wpkh_ins, outs)
    sigs = []
    for i, priv in enumerate(privs):
        # sign each of 3 inputs
        tx = bitcoin.p2wpkh_sign(tx,
                                 i,
                                 binascii.hexlify(priv),
                                 amount,
                                 native=True)
        # check that verify_tx_input correctly validates;
        # to do this, we need to extract the signature and get the scriptCode
        # of this pubkey
        scriptCode = bitcoin.pubkey_to_p2pkh_script(pubs[i])
        witness = bitcoin.deserialize(tx)['ins'][i]['txinwitness']
        assert len(witness) == 2
        assert witness[1] == pubs[i]
        sig = witness[0]
        assert bitcoin.verify_tx_input(tx,
                                       i,
                                       scriptPubKeys[i],
                                       sig,
                                       pubs[i],
                                       scriptCode=scriptCode,
                                       amount=amount)
    txid = jm_single().bc_interface.pushtx(tx)
    assert txid
Пример #19
0
def create_coinjoin_proposal(bobdata, alicedata, verbose=True, incentive=0):
    """A very crude/static implementation of a coinjoin for SNICKER.
    **VERY DELIBERATELY STUPIDLY SIMPLE VERSION!**
    We assume only one utxo for each side (this will certainly change, Alice
    side, for flexibility). Two outputs equal size are created with 1 change
    for Alice also (Bob's utxo is completely satisfied by 1 output).
    The data for each side is utxo, and amount; alice must provide
    privkey for partial sign. All scriptpubkeys assumed p2sh/p2wpkh for now.
    Bob's destination is tweaked and included as a destination which he
    will verify.
    What is returned is (tweak, partially signed tx) which is enough information
    for Bob to complete.
    """
    fee = estimate_tx_fee(2, 3, 'p2sh-p2wpkh')
    bob_utxo, bob_pubkey, amount = bobdata
    alice_utxo, alice_privkey, alice_amount, alice_destination, change = alicedata
    ins = [bob_utxo, alice_utxo]
    random.shuffle(ins)
    tweak, dest_pt, bob_destination = create_recipient_address(bob_pubkey,
                                                               segwit=True)
    print('using amount, alice_amount,incentive, fee: ' +
          ','.join([str(x) for x in [amount, alice_amount, incentive, fee]]))
    coinjoin_amount = amount + incentive
    change_amount = alice_amount - coinjoin_amount - incentive - fee
    outs = [{
        "address": alice_destination,
        "value": coinjoin_amount
    }, {
        "address": bob_destination,
        "value": coinjoin_amount
    }, {
        "address": change,
        "value": change_amount
    }]
    random.shuffle(outs)
    unsigned_tx = btc.mktx(ins, outs)
    if verbose:
        print('here is proposed transaction:\n',
              pformat(btc.deserialize(unsigned_tx)))
        print('destination for Bob: ', bob_destination)
        print('destination for Alice: ', alice_destination)
        print('destination for Alice change: ', change)
        if not raw_input("Is this acceptable? (y/n):") == "y":
            return (None, None)
    #Alice signs her input; assuming segwit here for now
    partially_signed_tx = btc.sign(unsigned_tx,
                                   1,
                                   alice_privkey,
                                   amount=alice_amount)
    #return the material to be sent to Bob
    return (tweak, partially_signed_tx)
Пример #20
0
def test_signing_simple(setup_wallet, wallet_cls, type_check):
    jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
    storage = VolatileStorage()
    wallet_cls.initialize(storage, get_network())
    wallet = wallet_cls(storage)
    utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0))
    tx = btc.deserialize(btc.mktx(['{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])],
                                  ['00'*17 + ':' + str(10**8 - 9000)]))
    binarize_tx(tx)
    script = wallet.get_script(0, 1, 0)
    wallet.sign_tx(tx, {0: (script, 10**8)})
    type_check(tx)
    txout = jm_single().bc_interface.pushtx(hexlify(btc.serialize(tx)).decode('ascii'))
    assert txout
Пример #21
0
def test_signing_simple(setup_wallet, wallet_cls, type_check):
    jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
    storage = VolatileStorage()
    wallet_cls.initialize(storage, get_network())
    wallet = wallet_cls(storage)
    utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0))
    # The dummy output is constructed as an unspendable p2sh:
    tx = btc.deserialize(btc.mktx(['{}:{}'.format(
        hexlify(utxo[0]).decode('ascii'), utxo[1])],
        [btc.p2sh_scriptaddr(b"\x00",magicbyte=196) + ':' + str(10**8 - 9000)]))
    script = wallet.get_script(0, 1, 0)
    tx = wallet.sign_tx(tx, {0: (script, 10**8)})
    type_check(tx)
    txout = jm_single().bc_interface.pushtx(btc.serialize(tx))
    assert txout
Пример #22
0
 def signature_form(self, index):
     """Construct the transaction template
     (confusingly, an entirely different notion of
     'transaction template' to the one seen lower down
     in this module; here we're talking about the template
     for sighashing in Bitcoin) for input at index index.
     Can only be called after all the keys for this
     input have been set, so that the signing redeem script
     exists; otherwise an Exception is raised.
     """
     assert self.signing_redeem_scripts[index]
     return btc.segwit_signature_form(btc.deserialize(self.base_form),
                                      index,
                                      self.signing_redeem_scripts[index],
                                      self.ins[index][1])
Пример #23
0
def test_signing_simple(setup_wallet, wallet_cls, type_check):
    jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
    storage = VolatileStorage()
    wallet_cls.initialize(storage, get_network())
    wallet = wallet_cls(storage)
    utxo = fund_wallet_addr(wallet, wallet.get_internal_addr(0))
    # The dummy output is of length 25 bytes, because, for SegwitWallet, we else
    # trigger the tx-size-small DOS limit in Bitcoin Core (82 bytes is the
    # smallest "normal" transaction size (non-segwit size, ie no witness)
    tx = btc.deserialize(
        btc.mktx(['{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])],
                 ['00' * 25 + ':' + str(10**8 - 9000)]))
    script = wallet.get_script(0, 1, 0)
    tx = wallet.sign_tx(tx, {0: (script, 10**8)})
    type_check(tx)
    txout = jm_single().bc_interface.pushtx(btc.serialize(tx))
    assert txout
Пример #24
0
def test_signing_imported(setup_wallet, wif, keytype, type_check):
    jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
    storage = VolatileStorage()
    SegwitLegacyWallet.initialize(storage, get_network())
    wallet = SegwitLegacyWallet(storage)

    MIXDEPTH = 0
    path = wallet.import_private_key(MIXDEPTH, wif, keytype)
    utxo = fund_wallet_addr(wallet, wallet.get_addr_path(path))
    tx = btc.deserialize(
        btc.mktx(['{}:{}'.format(hexlify(utxo[0]).decode('ascii'), utxo[1])],
                 ['00' * 17 + ':' + str(10**8 - 9000)]))
    script = wallet.get_script_path(path)
    tx = wallet.sign_tx(tx, {0: (script, 10**8)})
    type_check(tx)
    txout = jm_single().bc_interface.pushtx(btc.serialize(tx))
    assert txout
Пример #25
0
def make_sign_and_push(ins_full,
                       wallet_service,
                       amount,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL,
                       estimate_fee=False):
    """Utility function for easily building transactions
    from wallets
    """
    assert isinstance(wallet_service, WalletService)
    total = sum(x['value'] for x in ins_full.values())
    ins = list(ins_full.keys())
    #random output address and change addr
    output_addr = wallet_service.get_new_addr(
        1, 1) if not output_addr else output_addr
    change_addr = wallet_service.get_new_addr(
        0, 1) if not change_addr else change_addr
    fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000
    outs = [{
        'value': amount,
        'address': output_addr
    }, {
        'value': total - amount - fee_est,
        'address': change_addr
    }]

    de_tx = btc.deserialize(btc.mktx(ins, outs))
    scripts = {}
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        script = wallet_service.addr_to_script(ins_full[utxo]['address'])
        scripts[index] = (script, ins_full[utxo]['value'])
    binarize_tx(de_tx)
    de_tx = wallet_service.sign_tx(de_tx, scripts, hashcode=hashcode)
    #pushtx returns False on any error
    push_succeed = jm_single().bc_interface.pushtx(btc.serialize(de_tx))
    if push_succeed:
        txid = btc.txhash(btc.serialize(de_tx))
        # in normal operation this happens automatically
        # but in some tests there is no monitoring loop:
        wallet_service.process_new_tx(de_tx, txid)
        return txid
    else:
        return False
Пример #26
0
def test_signing_imported(setup_wallet, wif, keytype, type_check):
    jm_single().config.set('BLOCKCHAIN', 'network', 'testnet')
    storage = VolatileStorage()
    SegwitLegacyWallet.initialize(storage, get_network())
    wallet = SegwitLegacyWallet(storage)

    MIXDEPTH = 0
    path = wallet.import_private_key(MIXDEPTH, wif, keytype)
    utxo = fund_wallet_addr(wallet, wallet.get_address_from_path(path))
    # The dummy output is constructed as an unspendable p2sh:
    tx = btc.deserialize(btc.mktx(['{}:{}'.format(
        hexlify(utxo[0]).decode('ascii'), utxo[1])],
        [btc.p2sh_scriptaddr(b"\x00",magicbyte=196) + ':' + str(10**8 - 9000)]))
    script = wallet.get_script_from_path(path)
    tx = wallet.sign_tx(tx, {0: (script, 10**8)})
    type_check(tx)
    txout = jm_single().bc_interface.pushtx(btc.serialize(tx))
    assert txout
def test_serialization_roundtrip2():
    #Data extracted from:
    #https://github.com/bitcoin/bitcoin/blob/master/src/test/data/tx_valid.json
    #These are a variety of rather strange edge case transactions, which are
    #still valid.
    #Note that of course this is only a serialization, not validity test, so
    #only currently of very limited significance
    with open(os.path.join(testdir, "tx_valid.json"), "r") as f:
        json_data = f.read()
    valid_txs = json.loads(json_data)
    for j in valid_txs:
        #ignore comment entries
        if len(j) < 2:
            continue
        print j
        deserialized = btc.deserialize(str(j[0]))
        print deserialized
        assert j[0] == btc.serialize(deserialized)
Пример #28
0
 def find_secret_from_tx3_redeem(self, expected_txid=None):
     """Given a txid assumed to be a transaction which spends from TX1
     (so must be TX3 whether ours or theirs, since this is the only
     doubly-signed tx), and assuming it has been spent from (so this
     function is only called if redeeming TX3 fails), find the redeeming
     transaction and extract the coinswap secret from its scriptSig(s).
     The secret is returned.
     If expected_txid is provided, checks that this is the redeeming txid,
     in which case returns "True".
     """
     assert self.tx3.spending_tx
     deser_spending_tx = btc.deserialize(self.tx3.spending_tx)
     vins = deser_spending_tx['ins']
     self.secret = get_secret_from_vin(vins, self.hashed_secret)
     if not self.secret:
         cslog.info("Critical error; TX3 spent but no "
                   "coinswap secret was found.")
         return False
     return self.secret
Пример #29
0
 def __str__(self):
     """Convenience function for showing tx in current
     state in human readable form. This is not an object
     serialization (see serialize).
     """
     msg = []
     tx = self.base_form
     if not self.fully_signed_tx:
         msg.append("Not fully signed")
         msg.append("Signatures: " + str(self.signatures))
         if self.txid:
             msg.append("Txid: " + self.txid)
     else:
         msg.append("Fully signed.")
         if self.txid:
             msg.append("Txid: " + self.txid)
         tx = self.fully_signed_tx
     msg.append(tx)
     dtx = btc.deserialize(tx)
     return pformat(dtx) + "\n" + "\n".join(msg)
Пример #30
0
    def on_JM_TX_RECEIVED(self, nick, txhex, offer):
        # "none" flags p2ep protocol; pass through to the generic
        # on_tx handler for that:
        if offer == "none":
            return self.on_p2ep_tx_received(nick, txhex)

        offer = json.loads(offer)
        retval = self.client.on_tx_received(nick, txhex, offer)
        if not retval[0]:
            jlog.info("Maker refuses to continue on receipt of tx")
        else:
            sigs = retval[1]
            self.finalized_offers[nick] = offer
            tx = btc.deserialize(txhex)
            self.finalized_offers[nick]["txd"] = tx
            txid = btc.txhash(btc.serialize(tx))
            # we index the callback by the out-set of the transaction,
            # because the txid is not known until all scriptSigs collected
            # (hence this is required for Makers, but not Takers).
            # For more info see WalletService.transaction_monitor():
            txinfo = tuple((x["script"], x["value"]) for x in tx["outs"])
            self.client.wallet_service.register_callbacks(
                [self.unconfirm_callback], txinfo, "unconfirmed")
            self.client.wallet_service.register_callbacks(
                [self.confirm_callback], txinfo, "confirmed")

            task.deferLater(
                reactor,
                float(jm_single().config.getint("TIMEOUT",
                                                "unconfirm_timeout_sec")),
                self.client.wallet_service.check_callback_called, txinfo,
                self.unconfirm_callback, "unconfirmed",
                "transaction with outputs: " + str(txinfo) + " not broadcast.")

            d = self.callRemote(commands.JMTXSigs,
                                nick=nick,
                                sigs=json.dumps(sigs))
            self.defaultCallbacks(d)
        return {"accepted": True}