def test_bip143_tv():
    # p2sh-p2wpkh case:
    rawtx_hex = "0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000"
    inp_spk_hex = "a9144733f37cf4db86fbc2efed2500b4f4e49f31202387"
    value = 10
    redeemScript = "001479091972186c449eb1ded22b78e40d009bdf0089"
    privkey_hex = "eb696a065ef48a2192da5b28b694f87544b30fae8327c4510137a922f32c6dcf01"
    pubkey_hex = "03ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a26873"
    tx = btc.CMutableTransaction.deserialize(btc.x(rawtx_hex))
    btc.sign(tx, 0, btc.x(privkey_hex), amount=btc.coins_to_satoshi(10), native=False)
    expectedsignedtx = "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"
    assert btc.b2x(tx.serialize()) == expectedsignedtx
Example #2
0
def test_sign_standard_txs(addrtype):
    # liberally copied from python-bitcoinlib tests,
    # in particular see:
    # https://github.com/petertodd/python-bitcoinlib/pull/227

    # Create the (in)famous correct brainwallet secret key.
    priv = hashlib.sha256(b'correct horse battery staple').digest() + b"\x01"
    pub = btc.privkey_to_pubkey(priv)

    # Create an address from that private key.
    # (note that the input utxo is fake so we are really only creating
    # a destination here).
    scriptPubKey = btc.CScript([btc.OP_0, btc.Hash160(pub)])
    address = btc.P2WPKHCoinAddress.from_scriptPubKey(scriptPubKey)

    # Create a dummy outpoint; use same 32 bytes for convenience
    txid = priv[:32]
    vout = 2
    amount = btc.coins_to_satoshi(float('0.12345'))

    # Calculate an amount for the upcoming new UTXO. Set a high fee to bypass
    # bitcoind minfee setting.
    amount_less_fee = int(amount - btc.coins_to_satoshi(0.01))

    # Create a destination to send the coins.
    destination_address = address
    target_scriptPubKey = scriptPubKey

    # Create the unsigned transaction.
    txin = btc.CTxIn(btc.COutPoint(txid[::-1], vout))
    txout = btc.CTxOut(amount_less_fee, target_scriptPubKey)
    tx = btc.CMutableTransaction([txin], [txout])

    # Calculate the signature hash for the transaction. This is then signed by the
    # private key that controls the UTXO being spent here at this txin_index.
    if addrtype == "p2wpkh":
        sig, msg = btc.sign(tx, 0, priv, amount=amount, native="p2wpkh")
    elif addrtype == "p2sh-p2wpkh":
        sig, msg = btc.sign(tx, 0, priv, amount=amount, native=False)
    elif addrtype == "p2pkh":
        sig, msg = btc.sign(tx, 0, priv)
    else:
        assert False
    if not sig:
        print(msg)
        raise
    print("created signature: ", bintohex(sig))
    print("serialized transaction: {}".format(bintohex(tx.serialize())))
    print("deserialized transaction: {}\n".format(
        btc.human_readable_transaction(tx)))
Example #3
0
def sign(utxo, priv, destaddrs, utxo_address_type):
    """Sign a tx sending the amount amt, from utxo utxo,
    equally to each of addresses in list destaddrs,
    after fees; the purpose is to create multiple utxos.
    utxo_address_type must be one of p2sh-p2wpkh/p2wpkh/p2pkh.
    """
    results = validate_utxo_data([(utxo, priv)], retrieve=True,
                                 utxo_address_type=utxo_address_type)
    if not results:
        return False
    assert results[0][0] == utxo
    amt = results[0][1]
    ins = [utxo]
    estfee = estimate_tx_fee(1, len(destaddrs), txtype=utxo_address_type)
    outs = []
    share = int((amt - estfee) / len(destaddrs))
    fee = amt - share*len(destaddrs)
    assert fee >= estfee
    log.info("Using fee: " + str(fee))
    for i, addr in enumerate(destaddrs):
        outs.append({'address': addr, 'value': share})
    tx = btc.make_shuffled_tx(ins, outs, version=2, locktime=compute_tx_locktime())
    amtforsign = amt if utxo_address_type != "p2pkh" else None
    rawpriv, _ = BTCEngine.wif_to_privkey(priv)
    if utxo_address_type == "p2wpkh":
        native = utxo_address_type
    else:
        native = False
    success, msg = btc.sign(tx, 0, rawpriv, amount=amtforsign, native=native)
    assert success, msg
    return tx
Example #4
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
 def sign_transaction(cls, tx, index, privkey_locktime, amount,
                      hashcode=btc.SIGHASH_ALL, **kwargs):
     assert amount is not None
     priv, locktime = privkey_locktime
     pub = cls.privkey_to_pubkey(priv)
     redeem_script = cls.pubkey_to_script_code((pub, locktime))
     return btc.sign(tx, index, priv, amount=amount, native=redeem_script)
Example #6
0
def sign(utxo, priv, destaddrs, segwit=True):
    """Sign a tx sending the amount amt, from utxo utxo,
    equally to each of addresses in list destaddrs,
    after fees; the purpose is to create a large
    number of utxos. If segwit=True the (single) utxo is assumed to
    be of type segwit p2sh/p2wpkh.
    """
    results = validate_utxo_data([(utxo, priv)], retrieve=True, segwit=segwit)
    if not results:
        return False
    assert results[0][0] == utxo
    amt = results[0][1]
    ins = [utxo]
    # TODO extend to other utxo types
    txtype = 'p2sh-p2wpkh' if segwit else 'p2pkh'
    estfee = estimate_tx_fee(1, len(destaddrs), txtype=txtype)
    outs = []
    share = int((amt - estfee) / len(destaddrs))
    fee = amt - share * len(destaddrs)
    assert fee >= estfee
    log.info("Using fee: " + str(fee))
    for i, addr in enumerate(destaddrs):
        outs.append({'address': addr, 'value': share})
    unsigned_tx = btc.mktx(ins, outs)
    amtforsign = amt if segwit else None
    return btc.sign(unsigned_tx,
                    0,
                    btc.from_wif_privkey(priv, vbyte=get_p2pk_vbyte()),
                    amount=amtforsign)
Example #7
0
def create_single_acp_pair(utxo_in,
                           priv,
                           addr_out,
                           amount,
                           bump,
                           segwit=False):
    """Given a utxo and a signing key for it, and its amout in satoshis,
    sign a "transaction" consisting of only 1 input and one output, signed
    with single|acp sighash flags so it can be grafted into a bigger
    transaction.
    Also provide a destination address and a 'bump' value (so the creator
    can claim more output in the final transaction.
    Note that, for safety, bump *should* be positive if the recipient
    is untrusted, since otherwise they can waste your money by simply
    broadcasting this transaction without adding any inputs of their own.
    Returns the serialized 1 in, 1 out, and signed transaction.
    """
    assert bump >= 0, "Output of single|acp pair must be bigger than input for safety."
    out = {"address": addr_out, "value": amount + bump}
    tx = btc.mktx([utxo_in], [out])
    amt = amount if segwit else None
    return btc.sign(tx,
                    0,
                    priv,
                    hashcode=btc.SIGHASH_SINGLE | btc.SIGHASH_ANYONECANPAY,
                    amount=amt)
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(priv) for priv in privs]
    scriptPubKeys = [bitcoin.pubkey_to_p2wpkh_script(pub) for pub in pubs]
    addresses = [str(bitcoin.CCoinAddress.from_scriptPubKey(
        spk)) for spk in scriptPubKeys]
    #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 i, addr in enumerate(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))
        txhex = jm_single().bc_interface.get_transaction(txid)
        #wait for mining
        jm_single().bc_interface.tick_forward_chain(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)

    for i, priv in enumerate(privs):
        # sign each of 3 inputs; note that bitcoin.sign
        # automatically validates each signature it creates.
        sig, msg = bitcoin.sign(tx, i, priv, amount=amount, native="p2wpkh")
        if not sig:
            assert False, msg
    txid = jm_single().bc_interface.pushtx(tx.serialize())
    assert txid
Example #9
0
 def sign_transaction(cls, tx, index, privkey, *args, **kwargs):
     hashcode = kwargs.get('hashcode') or btc.SIGHASH_ALL
     return btc.sign(tx,
                     index,
                     privkey,
                     hashcode=hashcode,
                     amount=None,
                     native=False)
Example #10
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)
Example #11
0
 def sign_transaction(cls,
                      tx,
                      index,
                      privkey,
                      amount,
                      hashcode=btc.SIGHASH_ALL,
                      **kwargs):
     assert amount is not None
     return btc.sign(tx,
                     index,
                     privkey,
                     hashcode=hashcode,
                     amount=amount,
                     native="p2wpkh")
Example #12
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
Example #13
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.")
Example #14
0
def test_spend_freeze_script(setup_tx_creation):
    ensure_bip65_activated()

    wallet_service = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet']
    wallet_service.sync_wallet(fast=True)

    mediantime = jm_single().bc_interface.rpc("getblockchaininfo",
                                              [])["mediantime"]

    timeoffset_success_tests = [(2, False), (-60 * 60 * 24 * 30, True),
                                (60 * 60 * 24 * 30, False)]

    for timeoffset, required_success in timeoffset_success_tests:
        #generate keypair
        priv = b"\xaa" * 32 + b"\x01"
        pub = bitcoin.privkey_to_pubkey(priv)
        addr_locktime = mediantime + timeoffset
        redeem_script = bitcoin.mk_freeze_script(pub, addr_locktime)
        script_pub_key = bitcoin.redeem_script_to_p2wsh_script(redeem_script)
        # cannot convert to address within wallet service, as not known
        # to wallet; use engine directly:
        addr = wallet_service._ENGINE.script_to_address(script_pub_key)

        #fund frozen funds address
        amount = 100000000
        funding_ins_full = wallet_service.select_utxos(0, amount)
        funding_txid = make_sign_and_push(funding_ins_full,
                                          wallet_service,
                                          amount,
                                          output_addr=addr)
        assert funding_txid

        #spend frozen funds
        frozen_in = (funding_txid, 0)
        output_addr = wallet_service.get_internal_addr(1)
        miner_fee = 5000
        outs = [{'value': amount - miner_fee, 'address': output_addr}]
        tx = bitcoin.mktx([frozen_in], outs, locktime=addr_locktime + 1)
        i = 0
        sig, success = bitcoin.sign(tx,
                                    i,
                                    priv,
                                    amount=amount,
                                    native=redeem_script)
        assert success
        push_success = jm_single().bc_interface.pushtx(tx.serialize())
        assert push_success == required_success
Example #15
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
def test_on_sig(createcmtdata, dummyaddr, signmethod, schedule):
    #plan: create a new transaction with known inputs and dummy outputs;
    #then, create a signature with various inputs, pass in in b64 to on_sig.
    #in order for it to verify, the DummyBlockchainInterface will have to
    #return the right values in query_utxo_set

    #create 2 privkey + utxos that are to be ours
    privs = [x * 32 + "\x01" for x in [chr(y) for y in range(1, 6)]]
    utxos = [str(x) * 64 + ":1" for x in range(5)]
    fake_query_results = [{
        'value':
        200000000,
        'utxo':
        utxos[x],
        'address':
        bitcoin.privkey_to_address(privs[x], False, magicbyte=0x6f),
        'script':
        bitcoin.mk_pubkey_script(
            bitcoin.privkey_to_address(privs[x], False, magicbyte=0x6f)),
        'confirms':
        20
    } for x in range(5)]

    dbci = DummyBlockchainInterface()
    dbci.insert_fake_query_results(fake_query_results)
    jm_single().bc_interface = dbci
    #make a transaction with all the fake results above, and some outputs
    outs = [{
        'value': 100000000,
        'address': dummyaddr
    }, {
        'value': 899990000,
        'address': dummyaddr
    }]
    tx = bitcoin.mktx(utxos, outs)

    de_tx = bitcoin.deserialize(tx)
    #prepare the Taker with the right intermediate data
    taker = get_taker(schedule=schedule, sign_method=signmethod)
    taker.nonrespondants = ["cp1", "cp2", "cp3"]
    taker.latest_tx = de_tx
    #my inputs are the first 2 utxos
    taker.input_utxos = {
        utxos[0]: {
            'address': bitcoin.privkey_to_address(privs[0],
                                                  False,
                                                  magicbyte=0x6f),
            'value': 200000000
        },
        utxos[1]: {
            'address': bitcoin.privkey_to_address(privs[1],
                                                  False,
                                                  magicbyte=0x6f),
            'value': 200000000
        }
    }
    taker.utxos = {
        None: utxos[:2],
        "cp1": [utxos[2]],
        "cp2": [utxos[3]],
        "cp3": [utxos[4]]
    }
    for i in range(2):
        # placeholders required for my inputs
        taker.latest_tx['ins'][i]['script'] = 'deadbeef'
    #to prepare for my signing, need to mark cjaddr:
    taker.my_cj_addr = dummyaddr
    #make signatures for the last 3 fake utxos, considered as "not ours":
    tx3 = bitcoin.sign(tx, 2, privs[2])
    sig3 = b64encode(
        bitcoin.deserialize(tx3)['ins'][2]['script'].decode('hex'))
    taker.on_sig("cp1", sig3)
    tx4 = bitcoin.sign(tx, 3, privs[3])
    sig4 = b64encode(
        bitcoin.deserialize(tx4)['ins'][3]['script'].decode('hex'))
    taker.on_sig("cp2", sig4)
    tx5 = bitcoin.sign(tx, 4, privs[4])
    #Before completing with the final signature, which will trigger our own
    #signing, try with an injected failure of query utxo set, which should
    #prevent this signature being accepted.
    dbci.setQUSFail(True)
    sig5 = b64encode(
        bitcoin.deserialize(tx5)['ins'][4]['script'].decode('hex'))
    taker.on_sig("cp3", sig5)
    #allow it to succeed, and try again
    dbci.setQUSFail(False)
    #this should succeed and trigger the we-sign code
    taker.on_sig("cp3", sig5)
def make_sign_and_push(ins_sw,
                       wallet,
                       amount,
                       other_ins=None,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL):
    """A more complicated version of the function in test_tx_creation;
    will merge to this one once finished.
    ins_sw have this structure:
    {"txid:n":(amount, priv, index), "txid2:n2":(amount2, priv2, index2), ..}
    if other_ins is not None, it has the same format, 
    these inputs are assumed to be plain p2pkh.
    All of these inputs in these two sets will be consumed.
    They are ordered according to the "index" fields (to allow testing
    of specific ordering)
    It's assumed that they contain sufficient coins to satisy the
    required output specified in "amount", plus some extra for fees and a
    change output.
    The output_addr and change_addr, if None, are taken from the wallet
    and are ordinary p2pkh outputs.
    All amounts are in satoshis and only converted to btc for grab_coins
    """
    #total value of all inputs
    print ins_sw
    print other_ins
    total = sum([x[0] for x in ins_sw.values()])
    total += sum([x[0] for x in other_ins.values()])
    #construct the other inputs
    ins1 = other_ins
    ins1.update(ins_sw)
    ins1 = sorted(ins1.keys(), key=lambda k: ins1[k][2])
    #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
    outs = [{
        'value': amount,
        'address': output_addr
    }, {
        'value': total - amount - 10000,
        'address': change_addr
    }]
    tx = btc.mktx(ins1, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        temp_ins = ins_sw if utxo in ins_sw.keys() else other_ins
        amt, priv, n = temp_ins[utxo]
        temp_amt = amt if utxo in ins_sw.keys() else None
        #for better test code coverage
        print "signing tx index: " + str(index) + ", priv: " + priv
        if index % 2:
            priv = binascii.unhexlify(priv)
        ms = "other" if not temp_amt else "amount: " + str(temp_amt)
        print ms
        tx = btc.sign(tx, index, priv, hashcode=hashcode, amount=temp_amt)
    print pformat(btc.deserialize(tx))
    txid = jm_single().bc_interface.pushtx(tx)
    time.sleep(3)
    received = jm_single().bc_interface.get_received_by_addr(
        [output_addr], None)['data'][0]['balance']
    #check coins were transferred as expected
    assert received == amount
    #pushtx returns False on any error
    return txid
def test_on_sig(setup_taker, dummyaddr, schedule):
    #plan: create a new transaction with known inputs and dummy outputs;
    #then, create a signature with various inputs, pass in in b64 to on_sig.
    #in order for it to verify, the DummyBlockchainInterface will have to 
    #return the right values in query_utxo_set
    utxos = [(struct.pack(b"B", x) * 32, 1) for x in range(5)]
    #create 2 privkey + utxos that are to be ours
    privs = [x*32 + b"\x01" for x in [struct.pack(b'B', y) for y in range(1,6)]]
    scripts = [BTC_P2PKH.key_to_script(privs[x]) for x in range(5)]
    addrs = [BTC_P2PKH.privkey_to_address(privs[x]) for x in range(5)]
    fake_query_results = [{'value': 200000000, 'utxo': utxos[x], 'address': addrs[x],
                           'script': scripts[x], 'confirms': 20} for x in range(5)]

    dbci = DummyBlockchainInterface()
    dbci.insert_fake_query_results(fake_query_results)
    jm_single().bc_interface = dbci
    #make a transaction with all the fake results above, and some outputs
    outs = [{'value': 100000000, 'address': dummyaddr},
            {'value': 899990000, 'address': dummyaddr}]
    tx = bitcoin.mktx(utxos, outs)
    # since tx will be updated as it is signed, unlike in real life
    # (where maker signing operation doesn't happen here), we'll create
    # a second copy without the signatures:
    tx2 = bitcoin.mktx(utxos, outs)

    #prepare the Taker with the right intermediate data
    taker = get_taker(schedule=schedule)
    taker.nonrespondants=["cp1", "cp2", "cp3"]
    taker.latest_tx = tx
    #my inputs are the first 2 utxos
    taker.input_utxos = {utxos[0]:
                        {'address': addrs[0],
                         'script': scripts[0],
                         'value': 200000000},
                        utxos[1]:
                        {'address': addrs[1],
                         'script': scripts[1],
                         'value': 200000000}}    
    taker.utxos = {None: utxos[:2], "cp1": [utxos[2]], "cp2": [utxos[3]], "cp3":[utxos[4]]}
    for i in range(2):
        # placeholders required for my inputs
        taker.latest_tx.vin[i].scriptSig = bitcoin.CScript(hextobin('deadbeef'))
        tx2.vin[i].scriptSig = bitcoin.CScript(hextobin('deadbeef'))
    #to prepare for my signing, need to mark cjaddr:
    taker.my_cj_addr = dummyaddr
    #make signatures for the last 3 fake utxos, considered as "not ours":
    sig, msg = bitcoin.sign(tx2, 2, privs[2])
    assert sig, "Failed to sign: " + msg
    sig3 = b64encode(tx2.vin[2].scriptSig)
    taker.on_sig("cp1", sig3)
    #try sending the same sig again; should be ignored
    taker.on_sig("cp1", sig3)
    sig, msg = bitcoin.sign(tx2, 3, privs[3])
    assert sig, "Failed to sign: " + msg
    sig4 = b64encode(tx2.vin[3].scriptSig)
    #try sending junk instead of cp2's correct sig
    assert not taker.on_sig("cp2", str("junk")), "incorrectly accepted junk signature"
    taker.on_sig("cp2", sig4)
    sig, msg = bitcoin.sign(tx2, 4, privs[4])
    assert sig, "Failed to sign: " + msg
    #Before completing with the final signature, which will trigger our own
    #signing, try with an injected failure of query utxo set, which should
    #prevent this signature being accepted.
    dbci.setQUSFail(True)
    sig5 = b64encode(tx2.vin[4].scriptSig)
    assert not taker.on_sig("cp3", sig5), "incorrectly accepted sig5"
    #allow it to succeed, and try again
    dbci.setQUSFail(False)
    #this should succeed and trigger the we-sign code
    taker.on_sig("cp3", sig5)
Example #19
0
def graft_onto_single_acp(wallet, txhex, amount, destaddr):
    """Given a serialized txhex which is checked to be of
    form single|acp (one in, one out), a destination address
    and an amount to spend, grafts in this in-out pair (at index zero)
    to our own transaction spending amount amount to destination destaddr,
    and uses a user-specified transaction fee (normal joinmarket
    configuration), and sanity checks that the bump value is not
    greater than user specified bump option.
    Returned: serialized txhex of fully signed transaction.
    """
    d = btc.deserialize(txhex)
    if len(d['ins']) != 1 or len(d['outs']) != 1:
        return (False, "Proposed tx should have 1 in 1 out, has: " +
                ','.join([str(len(d[x])) for x in ['ins', 'outs']]))
    #most important part: check provider hasn't bumped more than options.bump:
    other_utxo_in = d['ins'][0]['outpoint']['hash'] + ":" + str(
        d['ins'][0]['outpoint']['index'])
    res = jm_single().bc_interface.query_utxo_set(other_utxo_in)
    assert len(res) == 1
    if not res[0]:
        return (False, "Utxo provided by counterparty not found.")
    excess = d['outs'][0]['value'] - res[0]["value"]
    if not excess <= options.bump:
        return (False,
                "Counterparty claims too much excess value: " + str(excess))
    #Last sanity check - ensure that it's single|acp, else we're wasting our time
    try:
        if 'txinwitness' in d['ins'][0]:
            sig, pub = d['ins'][0]['txinwitness']
        else:
            sig, pub = btc.deserialize_script(d['ins'][0]['script'])
        assert sig[-2:] == "83"
    except Exception as e:
        return (
            False, "The transaction's signature does not parse as signed with "
            "SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, for p2pkh or p2sh-p2wpkh, or "
            "is otherwise invalid, and so is not valid for this function.\n" +
            repr(e))
    #source inputs for our own chosen spending amount:
    try:
        input_utxos = wallet.select_utxos(options.mixdepth, amount)
    except Exception as e:
        return (False, "Unable to select sufficient coins from mixdepth: " +
                str(options.mixdepth))
    total_selected = sum([x['value'] for x in input_utxos.values()])
    fee = estimate_tx_fee(len(input_utxos) + 1, 3, txtype='p2sh-p2wpkh')
    change_amount = total_selected - amount - excess - fee
    changeaddr = wallet.get_new_addr(options.mixdepth, 1)
    #Build new transaction and, graft in signature
    ins = [other_utxo_in] + input_utxos.keys()
    outs = [
        d['outs'][0], {
            'address': destaddr,
            'value': amount
        }, {
            'address': changeaddr,
            'value': change_amount
        }
    ]
    fulltx = btc.mktx(ins, outs)
    df = btc.deserialize(fulltx)
    #put back in original signature
    df['ins'][0]['script'] = d['ins'][0]['script']
    if 'txinwitness' in d['ins'][0]:
        df['ins'][0]['txinwitness'] = d['ins'][0]['txinwitness']
    fulltx = btc.serialize(df)
    for i, iu in enumerate(input_utxos):
        priv, inamt = get_privkey_amount_from_utxo(wallet, iu)
        print("Signing index: ", i + 1, " with privkey: ", priv,
              " and amount: ", inamt, " for utxo: ", iu)
        fulltx = btc.sign(fulltx, i + 1, priv, amount=inamt)
    return (True, fulltx)