예제 #1
0
def cli_get_wallet(wallet_name, sync=True):
    walletclass = SegwitWallet if jm_single().config.get(
        "POLICY", "segwit") == "true" else Wallet
    if not os.path.exists(os.path.join('wallets', wallet_name)):
        wallet = walletclass(wallet_name,
                             None,
                             max_mix_depth=options.amtmixdepths)
    else:
        while True:
            try:
                pwd = get_password("Enter wallet decryption passphrase: ")
                wallet = walletclass(wallet_name,
                                     pwd,
                                     max_mix_depth=options.amtmixdepths)
            except WalletError:
                print("Wrong password, try again.")
                continue
            except Exception as e:
                print("Failed to load wallet, error message: " + repr(e))
                sys.exit(0)
            break
    if jm_single().config.get("BLOCKCHAIN",
                              "blockchain_source") == "electrum-server":
        jm_single().bc_interface.synctype = "with-script"
    if sync:
        sync_wallet(wallet, fast=options.fastsync)
    return wallet
예제 #2
0
def test_spend_p2sh_utxos(setup_tx_creation):
    #make a multisig address from 3 privs
    privs = [chr(x) * 32 + '\x01' for x in range(1, 4)]
    pubs = [
        bitcoin.privkey_to_pubkey(binascii.hexlify(priv)) for priv in privs
    ]
    script = bitcoin.mk_multisig_script(pubs, 2)
    msig_addr = bitcoin.scriptaddr(script, magicbyte=196)
    #pay into it
    wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet']
    sync_wallet(wallet)
    amount = 350000000
    ins_full = wallet.select_utxos(0, amount)
    txid = make_sign_and_push(ins_full, wallet, amount, output_addr=msig_addr)
    assert txid
    #wait for mining
    time.sleep(1)
    #spend out; the input can be constructed from the txid of previous
    msig_in = txid + ":0"
    ins = [msig_in]
    #random output address and change addr
    output_addr = wallet.get_new_addr(1, 1)
    amount2 = amount - 50000
    outs = [{'value': amount2, 'address': output_addr}]
    tx = bitcoin.mktx(ins, outs)
    sigs = []
    for priv in privs[:2]:
        sigs.append(bitcoin.multisign(tx, 0, script, binascii.hexlify(priv)))
    tx = bitcoin.apply_multisignatures(tx, 0, script, sigs)
    txid = jm_single().bc_interface.pushtx(tx)
    assert txid
예제 #3
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
예제 #4
0
def test_start_ygs(setup_ygrunner, num_ygs, wallet_structures, mean_amt,
                   malicious):
    """Set up some wallets, for the ygs and 1 sp.
    Then start the ygs in background and publish
    the seed of the sp wallet for easy import into -qt
    """
    wallets = make_wallets(num_ygs + 1,
                           wallet_structures=wallet_structures,
                           mean_amt=mean_amt)
    #the sendpayment bot uses the last wallet in the list
    wallet = wallets[num_ygs]['wallet']
    print("Seed : " + wallets[num_ygs]['seed'])
    #useful to see the utxos on screen sometimes
    sync_wallet(wallet, fast=True)
    print(wallet.unspent)
    txfee = 1000
    cjfee_a = 4200
    cjfee_r = '0.001'
    ordertype = 'swreloffer'
    minsize = 100000
    ygclass = MaliciousYieldGenerator if malicious else YieldGeneratorBasic
    for i in range(num_ygs):
        
        cfg = [txfee, cjfee_a, cjfee_r, ordertype, minsize]
        sync_wallet(wallets[i]["wallet"], fast=True)
        yg = ygclass(wallets[i]["wallet"], cfg)
        if malicious:
            yg.set_maliciousness(malicious)
        clientfactory = JMClientProtocolFactory(yg, proto_type="MAKER")
        nodaemon = jm_single().config.getint("DAEMON", "no_daemon")
        daemon = True if nodaemon == 1 else False
        rs = True if i == num_ygs - 1 else False
        start_reactor(jm_single().config.get("DAEMON", "daemon_host"),
                      jm_single().config.getint("DAEMON", "daemon_port"),
                      clientfactory, daemon=daemon, rs=rs)
예제 #5
0
def test_create_sighash_txs(setup_tx_creation):
    #non-standard hash codes:
    for sighash in [
            bitcoin.SIGHASH_ANYONECANPAY + bitcoin.SIGHASH_SINGLE,
            bitcoin.SIGHASH_NONE, bitcoin.SIGHASH_SINGLE
    ]:
        wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet']
        sync_wallet(wallet)
        amount = 350000000
        ins_full = wallet.select_utxos(0, amount)
        print "using hashcode: " + str(sighash)
        txid = make_sign_and_push(ins_full, wallet, amount, hashcode=sighash)
        assert txid

    #Create an invalid sighash single (too many inputs)
    extra = wallet.select_utxos(4, 100000000)  #just a few more inputs
    ins_full.update(extra)
    with pytest.raises(Exception) as e_info:
        txid = make_sign_and_push(ins_full,
                                  wallet,
                                  amount,
                                  hashcode=bitcoin.SIGHASH_SINGLE)

    #trigger insufficient funds
    with pytest.raises(Exception) as e_info:
        fake_utxos = wallet.select_utxos(4, 1000000000)
예제 #6
0
def sync_test_wallet(fast, wallet):
    sync_count = 0
    jm_single().bc_interface.wallet_synced = False
    while not jm_single().bc_interface.wallet_synced:
        sync_wallet(wallet, fast=fast)
        sync_count += 1
        # avoid infinite loop
        assert sync_count < 10
        log.debug("Tried " + str(sync_count) + " times")
 def final_checks():
     sync_wallet(wallet, fast=True)
     newbal = wallet.get_balance_by_mixdepth()[4]
     oldbal = wallet.get_balance_by_mixdepth()[1]
     # These are our check that the coinjoin succeeded
     assert newbal == coinjoin_amt
     # TODO: parametrize these; cj fees = 38K (.001 x 20M x 2 makers)
     # minus 1K tx fee contribution each; 600M is original balance
     # in mixdepth 1
     assert oldbal + newbal + (40000 - 2000) + taker.total_txfee == 600000000
예제 #8
0
def sync_wallets(wallets):
    for w in wallets:
        w.gap_limit = 0
        jm_single().bc_interface.wallet_synced = False
        for x in range(20):
            if jm_single().bc_interface.wallet_synced:
                break
            sync_wallet(w)
        else:
            assert False, "Failed to sync wallet"
예제 #9
0
def test_start_ygs(setup_ygrunner, num_ygs, wallet_structures, mean_amt,
                   malicious, deterministic):
    """Set up some wallets, for the ygs and 1 sp.
    Then start the ygs in background and publish
    the seed of the sp wallet for easy import into -qt
    """
    if jm_single().config.get("POLICY", "native") == "true":
        walletclass = SegwitWallet
    else:
        # TODO add Legacy
        walletclass = SegwitLegacyWallet

    wallets = make_wallets(num_ygs + 1,
                           wallet_structures=wallet_structures,
                           mean_amt=mean_amt,
                           walletclass=walletclass)
    #the sendpayment bot uses the last wallet in the list
    wallet = wallets[num_ygs]['wallet']
    jmprint("\n\nTaker wallet seed : " + wallets[num_ygs]['seed'])
    # for manual audit if necessary, show the maker's wallet seeds
    # also (note this audit should be automated in future, see
    # test_full_coinjoin.py in this directory)
    jmprint("\n\nMaker wallet seeds: ")
    for i in range(num_ygs):
        jmprint("Maker seed: " + wallets[i]['seed'])
    jmprint("\n")
    sync_wallet(wallet, fast=True)
    txfee = 1000
    cjfee_a = 4200
    cjfee_r = '0.001'
    ordertype = 'swreloffer'
    minsize = 100000
    ygclass = YieldGeneratorBasic
    if malicious:
        if deterministic:
            ygclass = DeterministicMaliciousYieldGenerator
        else:
            ygclass = MaliciousYieldGenerator
    for i in range(num_ygs):

        cfg = [txfee, cjfee_a, cjfee_r, ordertype, minsize]
        sync_wallet(wallets[i]["wallet"], fast=True)
        yg = ygclass(wallets[i]["wallet"], cfg)
        if malicious:
            yg.set_maliciousness(malicious, mtype="tx")
        clientfactory = JMClientProtocolFactory(yg, proto_type="MAKER")
        nodaemon = jm_single().config.getint("DAEMON", "no_daemon")
        daemon = True if nodaemon == 1 else False
        rs = True if i == num_ygs - 1 else False
        start_reactor(jm_single().config.get("DAEMON", "daemon_host"),
                      jm_single().config.getint("DAEMON", "daemon_port"),
                      clientfactory,
                      daemon=daemon,
                      rs=rs)
예제 #10
0
def main(finalizer=None, finalizer_args=None):
    #Will only be used by client
    log.startLogging(sys.stdout)
    load_coinjoinxt_config()
    #to allow testing of confirm/unconfirm callback for multiple txs
    if isinstance(cjxt_single().bc_interface, RegtestBitcoinCoreInterface):
        cjxt_single().bc_interface.tick_forward_chain_interval = 2
        cjxt_single().bc_interface.simulating = True

    wallet_name = sys.argv[1]
    server, port = sys.argv[2:4]
    #depth 0: spend in, depth 1: receive out, depth 2: for backout transactions.
    max_mix_depth = 3
    wallet_dir = os.path.join(cjxt_single().homedir, 'wallets')
    if not os.path.exists(os.path.join(wallet_dir, wallet_name)):
        wallet = SegwitWallet(wallet_name,
                              None,
                              max_mix_depth,
                              6,
                              wallet_dir=wallet_dir)
    else:
        while True:
            try:
                pwd = get_password("Enter wallet decryption passphrase: ")
                wallet = SegwitWallet(wallet_name,
                                      pwd,
                                      max_mix_depth,
                                      6,
                                      wallet_dir=wallet_dir)
            except WalletError:
                print("Wrong password, try again.")
                continue
            except Exception as e:
                print("Failed to load wallet, error message: " + repr(e))
                sys.exit(0)
            break
    """
    Uncomment this for auto-funding on regtest
    if isinstance(cjxt_single().bc_interface, RegtestBitcoinCoreInterface):
        #funding the wallet with outputs specifically suitable for the starting point.
        funding_utxo_addr = wallet.get_new_addr(0, 0, True)
        bob_promise_utxo_addr = wallet.get_new_addr(0, 0, True)
        cjxt_single().bc_interface.grab_coins(funding_utxo_addr, 1.0)
        cjxt_single().bc_interface.grab_coins(bob_promise_utxo_addr, 0.5)
    """
    sync_wallet(wallet, fast=False)
    wallet.used_coins = None
    factory = OCCServerProtocolFactory(wallet)
    start_daemon(server, port, factory)
    if finalizer:
        reactor.addSystemEventTrigger("after", "shutdown", finalizer,
                                      finalizer_args)
    reactor.run()
예제 #11
0
def test_absurd_fees(setup_tx_creation):
    """Test triggering of ValueError exception
    if the transaction fees calculated from the blockchain
    interface exceed the limit set in the config.
    """
    jm_single().bc_interface.absurd_fees = True
    #pay into it
    wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet']
    sync_wallet(wallet)
    amount = 350000000
    ins_full = wallet.select_utxos(0, amount)
    with pytest.raises(ValueError) as e_info:
        txid = make_sign_and_push(ins_full, wallet, amount, estimate_fee=True)
예제 #12
0
def test_all_same_priv(setup_tx_creation):
    #recipient
    priv = "aa" * 32 + "01"
    addr = bitcoin.privkey_to_address(priv, magicbyte=get_p2pk_vbyte())
    wallet = make_wallets(1, [[1, 0, 0, 0, 0]], 1)[0]['wallet']
    #make another utxo on the same address
    addrinwallet = wallet.get_addr(0, 0, 0)
    jm_single().bc_interface.grab_coins(addrinwallet, 1)
    sync_wallet(wallet)
    insfull = wallet.select_utxos(0, 110000000)
    outs = [{"address": addr, "value": 1000000}]
    ins = insfull.keys()
    tx = bitcoin.mktx(ins, outs)
    tx = bitcoin.signall(tx, wallet.get_key_from_addr(addrinwallet))
예제 #13
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
def test_create_sighash_txs(setup_tx_creation):
    #non-standard hash codes:
    for sighash in [bitcoin.SIGHASH_ANYONECANPAY + bitcoin.SIGHASH_SINGLE,
                    bitcoin.SIGHASH_NONE, bitcoin.SIGHASH_SINGLE]:
        wallet = make_wallets(1, [[2, 0, 0, 0, 1]], 3)[0]['wallet']
        sync_wallet(wallet, fast=True)
        amount = 350000000
        ins_full = wallet.select_utxos(0, amount)
        print("using hashcode: " + str(sighash))
        txid = make_sign_and_push(ins_full, wallet, amount, hashcode=sighash)
        assert txid

    #trigger insufficient funds
    with pytest.raises(Exception) as e_info:
        fake_utxos = wallet.select_utxos(4, 1000000000)
def test_create_p2sh_output_tx(setup_tx_creation, nw, wallet_structures,
                               mean_amt, sdev_amt, amount, pubs, k):
    wallets = make_wallets(nw, wallet_structures, mean_amt, sdev_amt)
    for w in wallets.values():
        sync_wallet(w['wallet'], fast=True)
    for k, w in enumerate(wallets.values()):
        wallet = w['wallet']
        ins_full = wallet.select_utxos(0, amount)
        script = bitcoin.mk_multisig_script(pubs, k)
        output_addr = bitcoin.script_to_address(script, vbyte=196)
        txid = make_sign_and_push(ins_full,
                                  wallet,
                                  amount,
                                  output_addr=output_addr)
        assert txid
예제 #16
0
def test_create_p2sh_output_tx(setup_tx_creation, nw, wallet_structures,
                               mean_amt, sdev_amt, amount, pubs, k):
    wallets = make_wallets(nw, wallet_structures, mean_amt, sdev_amt)
    for w in wallets.values():
        sync_wallet(w['wallet'])
    for k, w in enumerate(wallets.values()):
        wallet = w['wallet']
        ins_full = wallet.select_utxos(0, amount)
        script = bitcoin.mk_multisig_script(pubs, k)
        #try the alternative argument passing
        pubs.append(k)
        script2 = bitcoin.mk_multisig_script(*pubs)
        assert script2 == script
        output_addr = bitcoin.scriptaddr(script, magicbyte=196)
        txid = make_sign_and_push(ins_full,
                                  wallet,
                                  amount,
                                  output_addr=output_addr)
        assert txid
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 = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet']
    sync_wallet(wallet, fast=True)
    amount = 35000000
    p2wpkh_ins = []
    for addr in addresses:
        ins_full = wallet.select_utxos(0, amount)
        txid = make_sign_and_push(ins_full, wallet, amount, output_addr=addr)
        assert txid
        p2wpkh_ins.append(txid + ":0")
        #wait for mining
        time.sleep(1)
    #random output address
    output_addr = wallet.get_new_addr(1, 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
예제 #18
0
def test_query_utxo_set(setup_wallets):
    load_program_config()
    jm_single().bc_interface.tick_forward_chain_interval = 1
    wallet = create_wallet_for_sync([2, 3, 0, 0, 0],
                                    ["wallet4utxo.json", "4utxo", [2, 3]])
    sync_wallet(wallet, fast=True)
    txid = do_tx(wallet, 90000000)
    txid2 = do_tx(wallet, 20000000)
    print("Got txs: ", txid, txid2)
    res1 = jm_single().bc_interface.query_utxo_set(txid + ":0", includeunconf=True)
    res2 = jm_single().bc_interface.query_utxo_set(
        [txid + ":0", txid2 + ":1"],
        includeconf=True, includeunconf=True)
    assert len(res1) == 1
    assert len(res2) == 2
    assert all([x in res1[0] for x in ['script', 'address', 'value']])
    assert not 'confirms' in res1[0]
    assert 'confirms' in res2[0]
    assert 'confirms' in res2[1]
    res3 = jm_single().bc_interface.query_utxo_set("ee" * 32 + ":25")
    assert res3 == [None]
def test_spend_p2wsh(setup_tx_creation):
    #make 2 x 2 of 2multisig outputs; will need 4 privs
    privs = [struct.pack(b'B', x) * 32 + b'\x01' for x in range(1, 5)]
    privs = [binascii.hexlify(priv).decode('ascii') for priv in privs]
    pubs = [bitcoin.privkey_to_pubkey(priv) for priv in privs]
    redeemScripts = [bitcoin.mk_multisig_script(pubs[i:i+2], 2) for i in [0, 2]]
    scriptPubKeys = [bitcoin.pubkeys_to_p2wsh_script(pubs[i:i+2]) for i in [0, 2]]
    addresses = [bitcoin.pubkeys_to_p2wsh_address(pubs[i:i+2]) for i in [0, 2]]
    #pay into it
    wallet = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet']
    sync_wallet(wallet, fast=True)
    amount = 35000000
    p2wsh_ins = []
    for addr in addresses:
        ins_full = wallet.select_utxos(0, amount)
        txid = make_sign_and_push(ins_full, wallet, amount, output_addr=addr)
        assert txid
        p2wsh_ins.append(txid + ":0")
        #wait for mining
        time.sleep(1)
    #random output address and change addr
    output_addr = wallet.get_new_addr(1, 1)
    amount2 = amount*2 - 50000
    outs = [{'value': amount2, 'address': output_addr}]
    tx = bitcoin.mktx(p2wsh_ins, outs)
    sigs = []
    for i in range(2):
        sigs = []
        for priv in privs[i*2:i*2+2]:
            # sign input j with each of 2 keys
            sig = bitcoin.multisign(tx, i, redeemScripts[i], priv, amount=amount)
            sigs.append(sig)
            # check that verify_tx_input correctly validates;
            assert bitcoin.verify_tx_input(tx, i, scriptPubKeys[i], sig,
                                           bitcoin.privkey_to_pubkey(priv),
                                           scriptCode=redeemScripts[i], amount=amount)
        tx = bitcoin.apply_p2wsh_multisignatures(tx, i, redeemScripts[i], sigs)
    txid = jm_single().bc_interface.pushtx(tx)
    assert txid
def final_checks(wallets, amount, txfee, tsb, msb, source_mixdepth=0):
    """We use this to check that the wallet contents are
    as we've expected according to the test case.
    amount is the payment amount going from taker to maker.
    txfee is the bitcoin network transaction fee, paid by the
    taker.
    tsb, msb are taker and maker starting balances, each a tuple
    of two entries, source and destination mixdepth respectively.
    """
    jm_single().bc_interface.tickchain()
    for wallet in wallets:
        sync_wallet(wallet)
    takerbals = getbals(wallets[1], source_mixdepth)
    makerbals = getbals(wallets[0], source_mixdepth)
    # is the payment received?
    maker_newcoin_amt = makerbals[1] - msb[1]
    if not maker_newcoin_amt >= amount:
        print("Maker expected to receive at least: ", amount,
              " but got: ", maker_newcoin_amt)
        return False
    # assert that the maker received net exactly the right amount
    maker_spentcoin_amt = msb[0] - makerbals[0]
    if not maker_spentcoin_amt >= 0:
        # for now allow the non-cj fallback case
        print("maker's spent coin should have been positive, was: ", maker_spentcoin_amt)
        return False
    if not maker_newcoin_amt == amount + maker_spentcoin_amt:
        print("maker's new coins should have been: ", amount + maker_spentcoin_amt,
              " but was: ", maker_newcoin_amt)
        return False

    # Taker-side check
    # assert that the taker's total ending minus total starting
    # balance is the amount plus the txfee given.
    if not (sum(takerbals) - sum(tsb) + txfee + amount) == 0:
        print("Taker should have spent: ", txfee + amount,
              " but spent: ", sum(tsb) - sum(takerbals))
        return False
    return True
예제 #21
0
def test_blockr_sync(setup_blockr, net, seed, gaplimit, showprivkey, method):
    jm_single().config.set("BLOCKCHAIN", "network", net)
    wallet = Wallet(seed, None, max_mix_depth=5)
    sync_wallet(wallet)

    #copy pasted from wallet-tool; some boiled down form of
    #this should really be in wallet.py in the joinmarket module.
    def cus_print(s):
        print s

    total_balance = 0
    for m in range(wallet.max_mix_depth):
        cus_print('mixing depth %d m/0/%d/' % (m, m))
        balance_depth = 0
        for forchange in [0, 1]:
            cus_print(' ' + ('external' if forchange == 0 else 'internal') +
                      ' addresses m/0/%d/%d/' % (m, forchange))

            for k in range(wallet.index[m][forchange] + gaplimit):
                addr = wallet.get_addr(m, forchange, k)
                balance = 0.0
                for addrvalue in wallet.unspent.values():
                    if addr == addrvalue['address']:
                        balance += addrvalue['value']
                balance_depth += balance
                used = ('used' if k < wallet.index[m][forchange] else ' new')
                if showprivkey:
                    privkey = btc.wif_compressed_privkey(
                        wallet.get_key(m, forchange, k), get_p2pk_vbyte())
                else:
                    privkey = ''
                if (method == 'displayall' or balance > 0
                        or (used == ' new' and forchange == 0)):
                    cus_print(
                        '  m/0/%d/%d/%03d %-35s%s %.8f btc %s' %
                        (m, forchange, k, addr, used, balance / 1e8, privkey))
        total_balance += balance_depth
        print('for mixdepth=%d balance=%.8fbtc' % (m, balance_depth / 1e8))
    assert total_balance == 96085297
def test_wallet_sync_from_scratch(setup_wallets, wallet_structure, wallet_file,
                                  password, ic):
    """Simulate a scenario in which we use a new bitcoind, thusly:
    generate a new wallet and simply pretend that it has an existing
    index_cache. This will force import of all addresses up to
    the index_cache values.
    """
    wallet = create_wallet_for_sync(wallet_file, password, wallet_structure,
                                    [wallet_structure, wallet_file, password,
                                     ic])
    sync_count = 0
    jm_single().bc_interface.wallet_synced = False
    wallet.index_cache = ic
    while not jm_single().bc_interface.wallet_synced:
        wallet.index = []
        for i in range(5):
            wallet.index.append([0, 0])
        #will call with fast=False but index_cache exists; should use slow-sync
        sync_wallet(wallet)
        sync_count += 1
        #avoid infinite loop
        assert sync_count < 10
        log.debug("Tried " + str(sync_count) + " times")
    #after #586 we expect to ALWAYS succeed within 2 rounds
    assert sync_count <= 2
    #for each external branch, the new index may be higher than
    #the original index_cache if there was a higher used address
    expected_wallet_index = []
    for i, val in enumerate(wallet_structure):
        if val > wallet.index_cache[i][0]:
            expected_wallet_index.append([val, wallet.index_cache[i][1]])
        else:
            expected_wallet_index.append([wallet.index_cache[i][0],
                                          wallet.index_cache[i][1]])
    assert wallet.index == expected_wallet_index
    log.debug("This is wallet unspent: ")
    log.debug(json.dumps(wallet.unspent, indent=4))
예제 #23
0
def main():
    tumble_log = get_tumble_log(logsdir)
    (options, args) = get_tumbler_parser().parse_args()
    options_org = options
    options = vars(options)
    if len(args) < 1:
        print('Error: Needs a wallet file')
        sys.exit(0)
    load_program_config()

    #Load the wallet
    wallet_name = args[0]
    max_mix_depth = options['mixdepthsrc'] + options['mixdepthcount']
    wallet_path = get_wallet_path(wallet_name, None)
    wallet = open_test_wallet_maybe(wallet_path, wallet_name, max_mix_depth)
    if jm_single().config.get("BLOCKCHAIN",
                              "blockchain_source") == "electrum-server":
        jm_single().bc_interface.synctype = "with-script"
    while not jm_single().bc_interface.wallet_synced:
        sync_wallet(wallet, fast=options['fastsync'])

    maxcjfee = get_max_cj_fee_values(jm_single().config, options_org)
    log.info(
        "Using maximum coinjoin fee limits per maker of {:.4%}, {} sat".format(
            *maxcjfee))

    #Parse options and generate schedule
    #Output information to log files
    jm_single().mincjamount = options['mincjamount']
    destaddrs = args[1:]
    print(destaddrs)
    #If the --restart flag is set we read the schedule
    #from the file, and filter out entries that are
    #already complete
    if options['restart']:
        res, schedule = get_schedule(
            os.path.join(logsdir, options['schedulefile']))
        if not res:
            print("Failed to load schedule, name: " +
                  str(options['schedulefile']))
            print("Error was: " + str(schedule))
            sys.exit(0)
        #This removes all entries that are marked as done
        schedule = [s for s in schedule if s[5] != 1]
        if isinstance(schedule[0][5], str) and len(schedule[0][5]) == 64:
            #ensure last transaction is confirmed before restart
            tumble_log.info("WAITING TO RESTART...")
            txid = schedule[0][5]
            restart_waiter(txid + ":0")  #add 0 index because all have it
            #remove the already-done entry (this connects to the other TODO,
            #probably better *not* to truncate the done-already txs from file,
            #but simplest for now.
            schedule = schedule[1:]
        elif schedule[0][5] != 0:
            print("Error: first schedule entry is invalid.")
            sys.exit(0)
        with open(os.path.join(logsdir, options['schedulefile']), "wb") as f:
            f.write(schedule_to_text(schedule))
        tumble_log.info("TUMBLE RESTARTING")
    else:
        #Create a new schedule from scratch
        schedule = get_tumble_schedule(options, destaddrs)
        tumble_log.info("TUMBLE STARTING")
        with open(os.path.join(logsdir, options['schedulefile']), "wb") as f:
            f.write(schedule_to_text(schedule))
        print("Schedule written to logs/" + options['schedulefile'])
    tumble_log.info("With this schedule: ")
    tumble_log.info(pprint.pformat(schedule))

    print("Progress logging to logs/TUMBLE.log")

    def filter_orders_callback(orders_fees, cjamount):
        """Decide whether to accept fees
        """
        return tumbler_filter_orders_callback(orders_fees, cjamount, taker,
                                              options)

    def taker_finished(res, fromtx=False, waittime=0.0, txdetails=None):
        """on_finished_callback for tumbler; processing is almost entirely
        deferred to generic taker_finished in tumbler_support module, except
        here reactor signalling.
        """
        sfile = os.path.join(logsdir, options['schedulefile'])
        tumbler_taker_finished_update(taker, sfile, tumble_log, options, res,
                                      fromtx, waittime, txdetails)
        if not fromtx:
            reactor.stop()
        elif fromtx != "unconfirmed":
            reactor.callLater(waittime * 60,
                              clientfactory.getClient().clientStart)

    #to allow testing of confirm/unconfirm callback for multiple txs
    if isinstance(jm_single().bc_interface, RegtestBitcoinCoreInterface):
        jm_single().bc_interface.tick_forward_chain_interval = 10
        jm_single().bc_interface.simulating = True
        jm_single().maker_timeout_sec = 15

    #instantiate Taker with given schedule and run
    taker = Taker(wallet,
                  schedule,
                  order_chooser=options['order_choose_fn'],
                  max_cj_fee=maxcjfee,
                  callbacks=(filter_orders_callback, None, taker_finished),
                  tdestaddrs=destaddrs)
    clientfactory = JMClientProtocolFactory(taker)
    nodaemon = jm_single().config.getint("DAEMON", "no_daemon")
    daemon = True if nodaemon == 1 else False
    if jm_single().config.get("BLOCKCHAIN",
                              "network") in ["regtest", "testnet"]:
        startLogging(sys.stdout)
    start_reactor(jm_single().config.get("DAEMON", "daemon_host"),
                  jm_single().config.getint("DAEMON", "daemon_port"),
                  clientfactory,
                  daemon=daemon)
예제 #24
0
def wallet_tool_main(wallet_root_path):
    """Main wallet tool script function; returned is a string (output or error)
    """
    parser = get_wallettool_parser()
    (options, args) = parser.parse_args()
    walletclass = SegwitWallet if jm_single().config.get(
        "POLICY", "segwit") == "true" else Wallet
    # if the index_cache stored in wallet.json is longer than the default
    # then set maxmixdepth to the length of index_cache
    maxmixdepth_configured = True
    if not options.maxmixdepth:
        maxmixdepth_configured = False
        options.maxmixdepth = 5

    noseed_methods = ['generate', 'recover']
    methods = ['display', 'displayall', 'summary', 'showseed', 'importprivkey',
               'history', 'showutxos']
    methods.extend(noseed_methods)
    noscan_methods = ['showseed', 'importprivkey', 'dumpprivkey', 'signmessage']

    if len(args) < 1:
        parser.error('Needs a wallet file or method')
        sys.exit(0)

    if args[0] in noseed_methods:
        method = args[0]
    else:
        seed = args[0]
        method = ('display' if len(args) == 1 else args[1].lower())
        if not os.path.exists(os.path.join(wallet_root_path, seed)):
            wallet = walletclass(seed, None, options.maxmixdepth,
                            options.gaplimit, extend_mixdepth= not maxmixdepth_configured,
                            storepassword=(method == 'importprivkey'),
                            wallet_dir=wallet_root_path)
        else:
            while True:
                try:
                    pwd = get_password("Enter wallet decryption passphrase: ")
                    wallet = walletclass(seed, pwd,
                            options.maxmixdepth,
                            options.gaplimit,
                            extend_mixdepth=not maxmixdepth_configured,
                            storepassword=(method == 'importprivkey'),
                            wallet_dir=wallet_root_path)
                except WalletError:
                    print("Wrong password, try again.")
                    continue
                except Exception as e:
                    print("Failed to load wallet, error message: " + repr(e))
                    sys.exit(0)
                break
        if method not in noscan_methods:
            # if nothing was configured, we override bitcoind's options so that
            # unconfirmed balance is included in the wallet display by default
            if 'listunspent_args' not in jm_single().config.options('POLICY'):
                jm_single().config.set('POLICY','listunspent_args', '[0]')
            sync_wallet(wallet, fast=options.fastsync)
    #Now the wallet/data is prepared, execute the script according to the method
    if method == "display":
        return wallet_display(wallet, options.gaplimit, options.showprivkey)
    elif method == "displayall":
        return wallet_display(wallet, options.gaplimit, options.showprivkey, displayall=True)
    elif method == "summary":
        return wallet_display(wallet, options.gaplimit, options.showprivkey, summarized=True)
    elif method == "history":
        if not isinstance(jm_single().bc_interface, BitcoinCoreInterface):
            print('showing history only available when using the Bitcoin Core ' +
                    'blockchain interface')
            sys.exit(0)
        else:
            return wallet_fetch_history(wallet, options)
    elif method == "generate":
        retval = wallet_generate_recover("generate", wallet_root_path)
        return retval if retval else "Failed"
    elif method == "recover":
        retval = wallet_generate_recover("recover", wallet_root_path)
        return retval if retval else "Failed"
    elif method == "showutxos":
        return wallet_showutxos(wallet, options.showprivkey)
    elif method == "showseed":
        return wallet_showseed(wallet)
    elif method == "dumpprivkey":
        return wallet_dumpprivkey(wallet, options.hd_path)
    elif method == "importprivkey":
        #note: must be interactive (security)
        wallet_importprivkey(wallet, options.mixdepth)
        return "Key import completed."
    elif method == "signmessage":
        return wallet_signmessage(wallet, options.hd_path, args[1])
예제 #25
0
def ygmain(ygclass, txfee=1000, cjfee_a=200, cjfee_r=0.002, ordertype='swreloffer',
           nickserv_password='', minsize=100000, gaplimit=6):
    import sys

    parser = OptionParser(usage='usage: %prog [options] [wallet file]')
    parser.add_option('-o', '--ordertype', action='store', type='string',
                      dest='ordertype', default=ordertype,
                      help='type of order; can be either reloffer or absoffer')
    parser.add_option('-t', '--txfee', action='store', type='int',
                      dest='txfee', default=txfee,
                      help='minimum miner fee in satoshis')
    parser.add_option('-c', '--cjfee', action='store', type='string',
                      dest='cjfee', default='',
                      help='requested coinjoin fee in satoshis or proportion')
    parser.add_option('-p', '--password', action='store', type='string',
                      dest='password', default=nickserv_password,
                      help='irc nickserv password')
    parser.add_option('-s', '--minsize', action='store', type='int',
                      dest='minsize', default=minsize,
                      help='minimum coinjoin size in satoshis')
    parser.add_option('-g', '--gap-limit', action='store', type="int",
                      dest='gaplimit', default=gaplimit,
                      help='gap limit for wallet, default='+str(gaplimit))
    parser.add_option('--fast',
                      action='store_true',
                      dest='fastsync',
                      default=False,
                      help=('choose to do fast wallet sync, only for Core and '
                            'only for previously synced wallet'))
    parser.add_option('-m', '--mixdepth', action='store', type='int',
                      dest='mixdepth', default=None,
                      help="highest mixdepth to use")
    (options, args) = parser.parse_args()
    if len(args) < 1:
        parser.error('Needs a wallet')
        sys.exit(0)
    wallet_name = args[0]
    ordertype = options.ordertype
    txfee = options.txfee
    if ordertype in ('reloffer', 'swreloffer'):
        if options.cjfee != '':
            cjfee_r = options.cjfee
        # minimum size is such that you always net profit at least 20%
        #of the miner fee
        minsize = max(int(1.2 * txfee / float(cjfee_r)), options.minsize)
    elif ordertype in ('absoffer', 'swabsoffer'):
        if options.cjfee != '':
            cjfee_a = int(options.cjfee)
        minsize = options.minsize
    else:
        parser.error('You specified an incorrect offer type which ' +\
                     'can be either swreloffer or swabsoffer')
        sys.exit(0)
    nickserv_password = options.password

    load_program_config()

    wallet_path = get_wallet_path(wallet_name, 'wallets')
    wallet = open_test_wallet_maybe(
        wallet_path, wallet_name, options.mixdepth,
        gap_limit=options.gaplimit)

    if jm_single().config.get("BLOCKCHAIN", "blockchain_source") == "electrum-server":
        jm_single().bc_interface.synctype = "with-script"

    while not jm_single().bc_interface.wallet_synced:
        sync_wallet(wallet, fast=options.fastsync)

    maker = ygclass(wallet, [options.txfee, cjfee_a, cjfee_r,
                             options.ordertype, options.minsize])
    jlog.info('starting yield generator')
    clientfactory = JMClientProtocolFactory(maker, proto_type="MAKER")

    nodaemon = jm_single().config.getint("DAEMON", "no_daemon")
    daemon = True if nodaemon == 1 else False
    if jm_single().config.get("BLOCKCHAIN", "network") in ["regtest", "testnet"]:
        startLogging(sys.stdout)
    start_reactor(jm_single().config.get("DAEMON", "daemon_host"),
                      jm_single().config.getint("DAEMON", "daemon_port"),
                      clientfactory, daemon=daemon)
예제 #26
0
def main():
    parser = OptionParser(
        usage=
        'usage: %prog [options] [txid:n]',
        description="Adds one or more utxos to the list that can be used to make "
                    "commitments for anti-snooping. Note that this utxo, and its "
                    "PUBkey, will be revealed to makers, so consider the privacy "
                    "implication. "
                    
                    "It may be useful to those who are having trouble making "
                    "coinjoins due to several unsuccessful attempts (especially "
                    "if your joinmarket wallet is new). "
                    
                    "'Utxo' means unspent transaction output, it must not "
                    "already be spent. "
                    "The options -w, -r and -R offer ways to load these utxos "
                    "from a file or wallet. "
                    "If you enter a single utxo without these options, you will be "
                    "prompted to enter the private key here - it must be in "
                    "WIF compressed format. "

                    "BE CAREFUL about handling private keys! "
                    "Don't do this in insecure environments. "
                    
                    "Also note this ONLY works for standard (p2pkh or p2sh-p2wpkh) utxos."
    )
    parser.add_option(
        '-r',
        '--read-from-file',
        action='store',
        type='str',
        dest='in_file',
        help='name of plain text csv file containing utxos, one per line, format: '
        'txid:N, WIF-compressed-privkey'
    )
    parser.add_option(
        '-R',
        '--read-from-json',
        action='store',
        type='str',
        dest='in_json',
        help='name of json formatted file containing utxos with private keys, as '
        'output from "python wallet-tool.py -p walletname showutxos"'
        )
    parser.add_option(
        '-w',
        '--load-wallet',
        action='store',
        type='str',
        dest='loadwallet',
        help='name of wallet from which to load utxos and use as commitments.'
        )
    parser.add_option(
        '-g',
        '--gap-limit',
        action='store',
        type='int',
        dest='gaplimit',
        default = 6,
        help='Only to be used with -w; gap limit for Joinmarket wallet, default 6.'
    )
    parser.add_option(
        '-M',
        '--max-mixdepth',
        action='store',
        type='int',
        dest='maxmixdepth',
        default=5,
        help='Only to be used with -w; number of mixdepths for wallet, default 5.'
    )
    parser.add_option(
        '-d',
        '--delete-external',
        action='store_true',
        dest='delete_ext',
        help='deletes the current list of external commitment utxos',
        default=False
        )
    parser.add_option(
        '-v',
        '--validate-utxos',
        action='store_true',
        dest='validate',
        help='validate the utxos and pubkeys provided against the blockchain',
        default=False
    )
    parser.add_option(
        '-o',
        '--validate-only',
        action='store_true',
        dest='vonly',
        help='only validate the provided utxos (file or command line), not add',
        default=False
    )
    parser.add_option('--fast',
                      action='store_true',
                      dest='fastsync',
                      default=False,
                      help=('choose to do fast wallet sync, only for Core and '
                      'only for previously synced wallet'))
    (options, args) = parser.parse_args()
    load_program_config()
    #TODO; sort out "commit file location" global so this script can
    #run without this hardcoding:
    utxo_data = []
    if options.delete_ext:
        other = options.in_file or options.in_json or options.loadwallet
        if len(args) > 0 or other:
            if input("You have chosen to delete commitments, other arguments "
                         "will be ignored; continue? (y/n)") != 'y':
                jmprint("Quitting", "warning")
                sys.exit(0)
        c, e = get_podle_commitments()
        jmprint(pformat(e), "info")
        if input(
            "You will remove the above commitments; are you sure? (y/n): ") != 'y':
            jmprint("Quitting", "warning")
            sys.exit(0)
        update_commitments(external_to_remove=e)
        jmprint("Commitments deleted.", "important")
        sys.exit(0)

    #Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet,
    #csv file or json file.
    if options.loadwallet:
        wallet_path = get_wallet_path(options.loadwallet, None)
        wallet = open_wallet(wallet_path, gap_limit=options.gaplimit)
        while not jm_single().bc_interface.wallet_synced:
            sync_wallet(wallet, fast=options.fastsync)

        # minor note: adding a utxo from an external wallet for commitments, we
        # default to not allowing disabled utxos to avoid a privacy leak, so the
        # user would have to explicitly enable.
        for md, utxos in wallet.get_utxos_by_mixdepth_().items():
            for (txid, index), utxo in utxos.items():
                txhex = binascii.hexlify(txid).decode('ascii') + ':' + str(index)
                wif = wallet.get_wif_path(utxo['path'])
                utxo_data.append((txhex, wif))

    elif options.in_file:
        with open(options.in_file, "rb") as f:
            utxo_info = f.readlines()
        for ul in utxo_info:
            ul = ul.rstrip()
            if ul:
                u, priv = get_utxo_info(ul)
                if not u:
                    quit(parser, "Failed to parse utxo info: " + str(ul))
                utxo_data.append((u, priv))
    elif options.in_json:
        if not os.path.isfile(options.in_json):
            jmprint("File: " + options.in_json + " not found.", "error")
            sys.exit(0)
        with open(options.in_json, "rb") as f:
            try:
                utxo_json = json.loads(f.read())
            except:
                jmprint("Failed to read json from " + options.in_json, "error")
                sys.exit(0)
        for u, pva in iteritems(utxo_json):
            utxo_data.append((u, pva['privkey']))
    elif len(args) == 1:
        u = args[0]
        priv = input(
            'input private key for ' + u + ', in WIF compressed format : ')
        u, priv = get_utxo_info(','.join([u, priv]))
        if not u:
            quit(parser, "Failed to parse utxo info: " + u)
        utxo_data.append((u, priv))
    else:
        quit(parser, 'Invalid syntax')
    if options.validate or options.vonly:
        sw = False if jm_single().config.get("POLICY", "segwit") == "false" else True
        if not validate_utxo_data(utxo_data, segwit=sw):
            quit(parser, "Utxos did not validate, quitting")
    if options.vonly:
        sys.exit(0)
    
    #We are adding utxos to the external list
    assert len(utxo_data)
    add_ext_commitments(utxo_data)
예제 #27
0
def main():
    parser = OptionParser(
        usage='usage: %prog [options] [txid:n]',
        description=
        "Adds one or more utxos to the list that can be used to make "
        "commitments for anti-snooping. Note that this utxo, and its "
        "PUBkey, will be revealed to makers, so consider the privacy "
        "implication. "
        "It may be useful to those who are having trouble making "
        "coinjoins due to several unsuccessful attempts (especially "
        "if your joinmarket wallet is new). "
        "'Utxo' means unspent transaction output, it must not "
        "already be spent. "
        "The options -w, -r and -R offer ways to load these utxos "
        "from a file or wallet. "
        "If you enter a single utxo without these options, you will be "
        "prompted to enter the private key here - it must be in "
        "WIF compressed format. "
        "BE CAREFUL about handling private keys! "
        "Don't do this in insecure environments. "
        "Also note this ONLY works for standard (p2pkh) utxos.")
    parser.add_option(
        '-r',
        '--read-from-file',
        action='store',
        type='str',
        dest='in_file',
        help=
        'name of plain text csv file containing utxos, one per line, format: '
        'txid:N, WIF-compressed-privkey')
    parser.add_option(
        '-R',
        '--read-from-json',
        action='store',
        type='str',
        dest='in_json',
        help=
        'name of json formatted file containing utxos with private keys, as '
        'output from "python wallet-tool.py -u -p walletname showutxos"')
    parser.add_option(
        '-w',
        '--load-wallet',
        action='store',
        type='str',
        dest='loadwallet',
        help='name of wallet from which to load utxos and use as commitments.')
    parser.add_option(
        '-g',
        '--gap-limit',
        action='store',
        type='int',
        dest='gaplimit',
        default=6,
        help=
        'Only to be used with -w; gap limit for Joinmarket wallet, default 6.')
    parser.add_option(
        '-M',
        '--max-mixdepth',
        action='store',
        type='int',
        dest='maxmixdepth',
        default=5,
        help=
        'Only to be used with -w; number of mixdepths for wallet, default 5.')
    parser.add_option(
        '-d',
        '--delete-external',
        action='store_true',
        dest='delete_ext',
        help='deletes the current list of external commitment utxos',
        default=False)
    parser.add_option(
        '-v',
        '--validate-utxos',
        action='store_true',
        dest='validate',
        help='validate the utxos and pubkeys provided against the blockchain',
        default=False)
    parser.add_option(
        '-o',
        '--validate-only',
        action='store_true',
        dest='vonly',
        help='only validate the provided utxos (file or command line), not add',
        default=False)
    parser.add_option('--fast',
                      action='store_true',
                      dest='fastsync',
                      default=False,
                      help=('choose to do fast wallet sync, only for Core and '
                            'only for previously synced wallet'))
    (options, args) = parser.parse_args()
    load_program_config()
    #TODO; sort out "commit file location" global so this script can
    #run without this hardcoding:
    utxo_data = []
    if options.delete_ext:
        other = options.in_file or options.in_json or options.loadwallet
        if len(args) > 0 or other:
            if raw_input(
                    "You have chosen to delete commitments, other arguments "
                    "will be ignored; continue? (y/n)") != 'y':
                print "Quitting"
                sys.exit(0)
        c, e = get_podle_commitments()
        print pformat(e)
        if raw_input(
                "You will remove the above commitments; are you sure? (y/n): "
        ) != 'y':
            print "Quitting"
            sys.exit(0)
        update_commitments(external_to_remove=e)
        print "Commitments deleted."
        sys.exit(0)

    #Three options (-w, -r, -R) for loading utxo and privkey pairs from a wallet,
    #csv file or json file.
    if options.loadwallet:
        while True:
            pwd = get_password("Enter wallet decryption passphrase: ")
            try:
                wallet = Wallet(options.loadwallet, pwd, options.maxmixdepth,
                                options.gaplimit)
            except WalletError:
                print("Wrong password, try again.")
                continue
            except Exception as e:
                print("Failed to load wallet, error message: " + repr(e))
                sys.exit(0)
            break
        sync_wallet(wallet, fast=options.fastsync)
        unsp = {}
        for u, av in wallet.unspent.iteritems():
            addr = av['address']
            key = wallet.get_key_from_addr(addr)
            wifkey = btc.wif_compressed_privkey(key, vbyte=get_p2pk_vbyte())
            unsp[u] = {
                'address': av['address'],
                'value': av['value'],
                'privkey': wifkey
            }
        for u, pva in unsp.iteritems():
            utxo_data.append((u, pva['privkey']))
    elif options.in_file:
        with open(options.in_file, "rb") as f:
            utxo_info = f.readlines()
        for ul in utxo_info:
            ul = ul.rstrip()
            if ul:
                u, priv = get_utxo_info(ul)
                if not u:
                    quit(parser, "Failed to parse utxo info: " + str(ul))
                utxo_data.append((u, priv))
    elif options.in_json:
        if not os.path.isfile(options.in_json):
            print "File: " + options.in_json + " not found."
            sys.exit(0)
        with open(options.in_json, "rb") as f:
            try:
                utxo_json = json.loads(f.read())
            except:
                print "Failed to read json from " + options.in_json
                sys.exit(0)
        for u, pva in utxo_json.iteritems():
            utxo_data.append((u, pva['privkey']))
    elif len(args) == 1:
        u = args[0]
        priv = raw_input('input private key for ' + u +
                         ', in WIF compressed format : ')
        u, priv = get_utxo_info(','.join([u, priv]))
        if not u:
            quit(parser, "Failed to parse utxo info: " + u)
        utxo_data.append((u, priv))
    else:
        quit(parser, 'Invalid syntax')
    if options.validate or options.vonly:
        if not validate_utxo_data(utxo_data):
            quit(parser, "Utxos did not validate, quitting")
    if options.vonly:
        sys.exit(0)

    #We are adding utxos to the external list
    assert len(utxo_data)
    add_ext_commitments(utxo_data)
예제 #28
0
def main():
    parser = get_sendpayment_parser()
    (options, args) = parser.parse_args()
    load_program_config()
    if options.p2ep and len(args) != 3:
        parser.error("PayJoin requires exactly three arguments: "
                     "wallet, amount and destination address.")
        sys.exit(0)
    elif options.schedule == '' and len(args) != 3:
        parser.error("Joinmarket sendpayment (coinjoin) needs arguments:"
        " wallet, amount and destination address")
        sys.exit(0)

    #without schedule file option, use the arguments to create a schedule
    #of a single transaction
    sweeping = False
    if options.schedule == '':
        #note that sendpayment doesn't support fractional amounts, fractions throw
        #here.
        amount = int(args[1])
        if amount == 0:
            sweeping = True
        destaddr = args[2]
        mixdepth = options.mixdepth
        addr_valid, errormsg = validate_address(destaddr)
        if not addr_valid:
            jmprint('ERROR: Address invalid. ' + errormsg, "error")
            return
        schedule = [[options.mixdepth, amount, options.makercount,
                     destaddr, 0.0, 0]]
    else:
        if options.p2ep:
            parser.error("Schedule files are not compatible with PayJoin")
            sys.exit(0)
        result, schedule = get_schedule(options.schedule)
        if not result:
            log.error("Failed to load schedule file, quitting. Check the syntax.")
            log.error("Error was: " + str(schedule))
            sys.exit(0)
        mixdepth = 0
        for s in schedule:
            if s[1] == 0:
                sweeping = True
            #only used for checking the maximum mixdepth required
            mixdepth = max([mixdepth, s[0]])

    wallet_name = args[0]

    check_regtest()

    if options.pickorders:
        chooseOrdersFunc = pick_order
        if sweeping:
            jmprint('WARNING: You may have to pick offers multiple times', "warning")
            jmprint('WARNING: due to manual offer picking while sweeping', "warning")
    else:
        chooseOrdersFunc = options.order_choose_fn

    # Dynamically estimate a realistic fee if it currently is the default value.
    # At this point we do not know even the number of our own inputs, so
    # we guess conservatively with 2 inputs and 2 outputs each.
    if options.txfee == -1:
        options.txfee = max(options.txfee, estimate_tx_fee(2, 2,
                                        txtype="p2sh-p2wpkh"))
        log.debug("Estimated miner/tx fee for each cj participant: " + str(
            options.txfee))
    assert (options.txfee >= 0)

    maxcjfee = (1, float('inf'))
    if not options.p2ep and not options.pickorders and options.makercount != 0:
        maxcjfee = get_max_cj_fee_values(jm_single().config, options)
        log.info("Using maximum coinjoin fee limits per maker of {:.4%}, {} "
                 "sat".format(*maxcjfee))

    log.debug('starting sendpayment')

    max_mix_depth = max([mixdepth, options.amtmixdepths - 1])

    wallet_path = get_wallet_path(wallet_name, None)
    wallet = open_test_wallet_maybe(
        wallet_path, wallet_name, max_mix_depth, gap_limit=options.gaplimit)

    if jm_single().config.get("BLOCKCHAIN",
        "blockchain_source") == "electrum-server" and options.makercount != 0:
        jm_single().bc_interface.synctype = "with-script"
    #wallet sync will now only occur on reactor start if we're joining.
    while not jm_single().bc_interface.wallet_synced:
        sync_wallet(wallet, fast=options.fastsync)

    # From the estimated tx fees, check if the expected amount is a
    # significant value compared the the cj amount
    total_cj_amount = amount
    if total_cj_amount == 0:
        total_cj_amount = wallet.get_balance_by_mixdepth()[options.mixdepth]
        if total_cj_amount == 0:
            raise ValueError("No confirmed coins in the selected mixdepth. Quitting")
    exp_tx_fees_ratio = ((1 + options.makercount) * options.txfee) / total_cj_amount
    if exp_tx_fees_ratio > 0.05:
        jmprint('WARNING: Expected bitcoin network miner fees for this coinjoin'
            ' amount are roughly {:.1%}'.format(exp_tx_fees_ratio), "warning")
        if input('You might want to modify your tx_fee'
            ' settings in joinmarket.cfg. Still continue? (y/n):')[0] != 'y':
            sys.exit('Aborted by user.')
    else:
        log.info("Estimated miner/tx fees for this coinjoin amount: {:.1%}"
            .format(exp_tx_fees_ratio))

    if options.makercount == 0 and not options.p2ep:
        direct_send(wallet, amount, mixdepth, destaddr, options.answeryes)
        return

    if wallet.get_txtype() == 'p2pkh':
        jmprint("Only direct sends (use -N 0) are supported for "
              "legacy (non-segwit) wallets.", "error")
        return

    def filter_orders_callback(orders_fees, cjamount):
        orders, total_cj_fee = orders_fees
        log.info("Chose these orders: " +pprint.pformat(orders))
        log.info('total cj fee = ' + str(total_cj_fee))
        total_fee_pc = 1.0 * total_cj_fee / cjamount
        log.info('total coinjoin fee = ' + str(float('%.3g' % (
            100.0 * total_fee_pc))) + '%')
        WARNING_THRESHOLD = 0.02  # 2%
        if total_fee_pc > WARNING_THRESHOLD:
            log.info('\n'.join(['=' * 60] * 3))
            log.info('WARNING   ' * 6)
            log.info('\n'.join(['=' * 60] * 1))
            log.info('OFFERED COINJOIN FEE IS UNUSUALLY HIGH. DOUBLE/TRIPLE CHECK.')
            log.info('\n'.join(['=' * 60] * 1))
            log.info('WARNING   ' * 6)
            log.info('\n'.join(['=' * 60] * 3))
        if not options.answeryes:
            if input('send with these orders? (y/n):')[0] != 'y':
                return False
        return True

    def taker_finished(res, fromtx=False, waittime=0.0, txdetails=None):
        if fromtx == "unconfirmed":
            #If final entry, stop *here*, don't wait for confirmation
            if taker.schedule_index + 1 == len(taker.schedule):
                reactor.stop()
            return
        if fromtx:
            if res:
                txd, txid = txdetails
                taker.wallet.remove_old_utxos(txd)
                taker.wallet.add_new_utxos(txd, txid)
                reactor.callLater(waittime*60,
                                  clientfactory.getClient().clientStart)
            else:
                #a transaction failed; we'll try to repeat without the
                #troublemakers.
                #If this error condition is reached from Phase 1 processing,
                #and there are less than minimum_makers honest responses, we
                #just give up (note that in tumbler we tweak and retry, but
                #for sendpayment the user is "online" and so can manually
                #try again).
                #However if the error is in Phase 2 and we have minimum_makers
                #or more responses, we do try to restart with the honest set, here.
                if taker.latest_tx is None:
                    #can only happen with < minimum_makers; see above.
                    log.info("A transaction failed but there are insufficient "
                             "honest respondants to continue; giving up.")
                    reactor.stop()
                    return
                #This is Phase 2; do we have enough to try again?
                taker.add_honest_makers(list(set(
                    taker.maker_utxo_data.keys()).symmetric_difference(
                        set(taker.nonrespondants))))
                if len(taker.honest_makers) < jm_single().config.getint(
                    "POLICY", "minimum_makers"):
                    log.info("Too few makers responded honestly; "
                             "giving up this attempt.")
                    reactor.stop()
                    return
                jmprint("We failed to complete the transaction. The following "
                      "makers responded honestly: " + str(taker.honest_makers) +\
                      ", so we will retry with them.", "warning")
                #Now we have to set the specific group we want to use, and hopefully
                #they will respond again as they showed honesty last time.
                #we must reset the number of counterparties, as well as fix who they
                #are; this is because the number is used to e.g. calculate fees.
                #cleanest way is to reset the number in the schedule before restart.
                taker.schedule[taker.schedule_index][2] = len(taker.honest_makers)
                log.info("Retrying with: " + str(taker.schedule[
                    taker.schedule_index][2]) + " counterparties.")
                #rewind to try again (index is incremented in Taker.initialize())
                taker.schedule_index -= 1
                taker.set_honest_only(True)
                reactor.callLater(5.0, clientfactory.getClient().clientStart)
        else:
            if not res:
                log.info("Did not complete successfully, shutting down")
            #Should usually be unreachable, unless conf received out of order;
            #because we should stop on 'unconfirmed' for last (see above)
            else:
                log.info("All transactions completed correctly")
            reactor.stop()

    if options.p2ep:
        # This workflow requires command line reading; we force info level logging
        # to remove noise, and mostly communicate to the user with the fn
        # log.info (directly or via default taker_info_callback).
        set_logging_level("INFO")
        # in the case where the payment just hangs for a long period, allow
        # it to fail gracefully with an information message; this is triggered
        # only by the stallMonitor, which gives up after 20*maker_timeout_sec:
        def p2ep_on_finished_callback(res, fromtx=False, waittime=0.0,
                                      txdetails=None):
            log.error("PayJoin payment was NOT made, timed out.")
            reactor.stop()
        taker = P2EPTaker(options.p2ep, wallet, schedule,
                          callbacks=(None, None, p2ep_on_finished_callback))
    else:
        taker = Taker(wallet,
                      schedule,
                      order_chooser=chooseOrdersFunc,
                      max_cj_fee=maxcjfee,
                      callbacks=(filter_orders_callback, None, taker_finished))
    clientfactory = JMClientProtocolFactory(taker)
    nodaemon = jm_single().config.getint("DAEMON", "no_daemon")
    daemon = True if nodaemon == 1 else False
    p2ep = True if options.p2ep != "" else False
    if jm_single().config.get("BLOCKCHAIN", "network") in ["regtest", "testnet"]:
        startLogging(sys.stdout)
    start_reactor(jm_single().config.get("DAEMON", "daemon_host"),
                  jm_single().config.getint("DAEMON", "daemon_port"),
                  clientfactory, daemon=daemon, p2ep=p2ep)
예제 #29
0
def wallet_tool_main(wallet_root_path):
    """Main wallet tool script function; returned is a string (output or error)
    """
    parser = get_wallettool_parser()
    (options, args) = parser.parse_args()

    noseed_methods = ['generate', 'recover']
    methods = ['display', 'displayall', 'summary', 'showseed', 'importprivkey',
               'history', 'showutxos']
    methods.extend(noseed_methods)
    noscan_methods = ['showseed', 'importprivkey', 'dumpprivkey', 'signmessage']
    readonly_methods = ['display', 'displayall', 'summary', 'showseed',
                        'history', 'showutxos', 'dumpprivkey', 'signmessage']

    if len(args) < 1:
        parser.error('Needs a wallet file or method')
        sys.exit(0)

    if options.mixdepth is not None and options.mixdepth < 0:
        parser.error("Must have at least one mixdepth.")
        sys.exit(0)

    if args[0] in noseed_methods:
        method = args[0]
        if options.mixdepth is None:
            options.mixdepth = DEFAULT_MIXDEPTH
    else:
        seed = args[0]
        wallet_path = get_wallet_path(seed, wallet_root_path)
        method = ('display' if len(args) == 1 else args[1].lower())
        read_only = method in readonly_methods

        wallet = open_test_wallet_maybe(
            wallet_path, seed, options.mixdepth, read_only=read_only,
            gap_limit=options.gaplimit)

        if method not in noscan_methods:
            # if nothing was configured, we override bitcoind's options so that
            # unconfirmed balance is included in the wallet display by default
            if 'listunspent_args' not in jm_single().config.options('POLICY'):
                jm_single().config.set('POLICY','listunspent_args', '[0]')
            while not jm_single().bc_interface.wallet_synced:
                sync_wallet(wallet, fast=options.fastsync)
    #Now the wallet/data is prepared, execute the script according to the method
    if method == "display":
        return wallet_display(wallet, options.gaplimit, options.showprivkey)
    elif method == "displayall":
        return wallet_display(wallet, options.gaplimit, options.showprivkey,
                              displayall=True)
    elif method == "summary":
        return wallet_display(wallet, options.gaplimit, options.showprivkey, summarized=True)
    elif method == "history":
        if not isinstance(jm_single().bc_interface, BitcoinCoreInterface):
            print('showing history only available when using the Bitcoin Core ' +
                    'blockchain interface')
            sys.exit(0)
        else:
            return wallet_fetch_history(wallet, options)
    elif method == "generate":
        retval = wallet_generate_recover("generate", wallet_root_path,
                                         mixdepth=options.mixdepth)
        return retval if retval else "Failed"
    elif method == "recover":
        retval = wallet_generate_recover("recover", wallet_root_path,
                                         mixdepth=options.mixdepth)
        return retval if retval else "Failed"
    elif method == "showutxos":
        return wallet_showutxos(wallet, options.showprivkey)
    elif method == "showseed":
        return wallet_showseed(wallet)
    elif method == "dumpprivkey":
        return wallet_dumpprivkey(wallet, options.hd_path)
    elif method == "importprivkey":
        #note: must be interactive (security)
        if options.mixdepth is None:
            parser.error("You need to specify a mixdepth with -m")
        wallet_importprivkey(wallet, options.mixdepth,
                             map_key_type(options.key_type))
        return "Key import completed."
    elif method == "signmessage":
        return wallet_signmessage(wallet, options.hd_path, args[2])
예제 #30
0
                              6,
                              wallet_dir=wallet_dir)
    else:
        while True:
            try:
                pwd = get_password("Enter wallet decryption passphrase: ")
                wallet = SegwitWallet(wallet_name,
                                      pwd,
                                      max_mix_depth,
                                      6,
                                      wallet_dir=wallet_dir)
            except WalletError:
                print("Wrong password, try again.")
                continue
            except Exception as e:
                print("Failed to load wallet, error message: " + repr(e))
                sys.exit(0)
            break
    """Uncomment this for auto-funding on regtest.
    if isinstance(cjxt_single().bc_interface, RegtestBitcoinCoreInterface):
        #funding the wallet with outputs specifically suitable for the starting point.
        funding_utxo_addr = wallet.get_new_addr(0, 0, True)
        alice_promise_utxo_addr = wallet.get_new_addr(0, 0, True)
        cjxt_single().bc_interface.grab_coins(funding_utxo_addr, 1.0)
        cjxt_single().bc_interface.grab_coins(alice_promise_utxo_addr, 0.3)
    """
    sync_wallet(wallet, fast=False)
    factory = OCCClientProtocolFactory(wallet)
    start_reactor(serv, port, factory)
    print('done')