Esempio n. 1
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)
def test_start_payjoin_server(setup_payjoin_server):
    # set up the wallet that the server owns, and the wallet for
    # the sender too (print the seed):
    if jm_single().config.get("POLICY", "native") == "true":
        walletclass = SegwitWallet
    else:
        walletclass = SegwitLegacyWallet

    wallet_services = make_wallets(2,
                                   wallet_structures=[[1, 3, 0, 0, 0]] * 2,
                                   mean_amt=2,
                                   walletclass=walletclass)
    #the server bot uses the first wallet, the sender the second
    server_wallet_service = wallet_services[0]['wallet']
    jmprint("\n\nTaker wallet seed : " + wallet_services[1]['seed'])
    jmprint("\n")
    server_wallet_service.sync_wallet(fast=True)
    
    site = Site(PayjoinServer(server_wallet_service))
    # TODO for now, just sticking with TLS test as non-encrypted
    # is unlikely to be used, but add that option.
    reactor.listenSSL(8080, site, contextFactory=get_ssl_context())
    #endpoint = endpoints.TCP4ServerEndpoint(reactor, 8080)
    #endpoint.listen(site)
    reactor.run()
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

    wallet_services = 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_service = wallet_services[num_ygs]['wallet']
    jmprint("\n\nTaker wallet seed : " + wallet_services[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: " + wallet_services[i]['seed'])
    jmprint("\n")
    wallet_service.sync_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]
        wallet_service_yg = wallet_services[i]["wallet"]
        wallet_service_yg.startService()
        yg = ygclass(wallet_service_yg, 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)
def test_spend_p2sh_p2wpkh_multi(setup_segwit, wallet_structure, in_amt,
                                 amount, segwit_amt, segwit_ins, o_ins):
    """Creates a wallet from which non-segwit inputs/
    outputs can be created, constructs one or more
    p2wpkh in p2sh spendable utxos (by paying into the
    corresponding address) and tests spending them
    in combination.
    wallet_structure is in accordance with commontest.make_wallets, see docs there
    in_amt is the amount to pay into each address into the wallet (non-segwit adds)
    amount (in satoshis) is how much we will pay to the output address
    segwit_amt in BTC is the amount we will fund each new segwit address with
    segwit_ins is a list of input indices (where to place the funding segwit utxos)
    other_ins is a list of input indices (where to place the funding non-sw utxos)
    """
    wallet = make_wallets(1, wallet_structure, in_amt,
                          walletclass=Wallet)[0]['wallet']
    jm_single().bc_interface.sync_wallet(wallet)
    other_ins = {}
    ctr = 0
    for k, v in wallet.unspent.iteritems():
        #only extract as many non-segwit utxos as we need;
        #doesn't matter which they are
        if ctr == len(o_ins):
            break
        other_ins[k] = (v["value"], wallet.get_key_from_addr(v["address"]),
                        o_ins[ctr])
        ctr += 1
    ins_sw = {}
    for i in range(len(segwit_ins)):
        #build segwit ins from "deterministic-random" keys;
        #intended to be the same for each run with the same parameters
        seed = json.dumps(
            [i, wallet_structure, in_amt, amount, segwit_ins, other_ins])
        priv = btc.sha256(seed) + "01"
        pub = btc.privtopub(priv)
        #magicbyte is testnet p2sh
        addr1 = btc.pubkey_to_p2sh_p2wpkh_address(pub, magicbyte=196)
        print "got address for p2shp2wpkh: " + addr1
        txid = jm_single().bc_interface.grab_coins(addr1, segwit_amt)
        #TODO - int cast, fix?
        ins_sw[get_utxo_from_txid(txid, addr1)] = (int(segwit_amt * 100000000),
                                                   priv, segwit_ins[i])
    #make_sign_and_push will sanity check the received amount is correct
    txid = make_sign_and_push(ins_sw, wallet, amount, other_ins)
    #will always be False if it didn't push.
    assert txid
Esempio n. 5
0
def test_spend_p2sh_p2wpkh_multi(setup_segwit, wallet_structure, in_amt, amount,
                                 segwit_amt, segwit_ins, o_ins):
    """Creates a wallet from which non-segwit inputs/
    outputs can be created, constructs one or more
    p2wpkh in p2sh spendable utxos (by paying into the
    corresponding address) and tests spending them
    in combination.
    wallet_structure is in accordance with commontest.make_wallets, see docs there
    in_amt is the amount to pay into each address into the wallet (non-segwit adds)
    amount (in satoshis) is how much we will pay to the output address
    segwit_amt in BTC is the amount we will fund each new segwit address with
    segwit_ins is a list of input indices (where to place the funding segwit utxos)
    other_ins is a list of input indices (where to place the funding non-sw utxos)
    """
    MIXDEPTH = 0

    # set up wallets and inputs
    nsw_wallet = make_wallets(1, wallet_structure, in_amt,
                              walletclass=LegacyWallet)[0]['wallet']
    jm_single().bc_interface.sync_wallet(nsw_wallet, fast=True)
    sw_wallet = make_wallets(1, [[len(segwit_ins), 0, 0, 0, 0]], segwit_amt)[0]['wallet']
    jm_single().bc_interface.sync_wallet(sw_wallet, fast=True)

    nsw_utxos = nsw_wallet.get_utxos_by_mixdepth_()[MIXDEPTH]
    sw_utxos = sw_wallet.get_utxos_by_mixdepth_()[MIXDEPTH]
    assert len(o_ins) <= len(nsw_utxos), "sync failed"
    assert len(segwit_ins) <= len(sw_utxos), "sync failed"

    total_amt_in_sat = 0

    nsw_ins = {}
    for nsw_in_index in o_ins:
        total_amt_in_sat += in_amt * 10**8
        nsw_ins[nsw_in_index] = nsw_utxos.popitem()

    sw_ins = {}
    for sw_in_index in segwit_ins:
        total_amt_in_sat += int(segwit_amt * 10**8)
        sw_ins[sw_in_index] = sw_utxos.popitem()

    all_ins = {}
    all_ins.update(nsw_ins)
    all_ins.update(sw_ins)

    # sanity checks
    assert len(all_ins) == len(nsw_ins) + len(sw_ins), \
        "test broken, duplicate index"
    for k in all_ins:
        assert 0 <= k < len(all_ins), "test broken, missing input index"

    # FIXME: encoding mess, mktx should accept binary input formats
    tx_ins = []
    for i, (txid, data) in sorted(all_ins.items(), key=lambda x: x[0]):
        tx_ins.append('{}:{}'.format(binascii.hexlify(txid[0]), txid[1]))

    # create outputs
    FEE = 50000
    assert FEE < total_amt_in_sat - amount, "test broken, not enough funds"

    cj_script = nsw_wallet.get_new_script(MIXDEPTH + 1, True)
    change_script = nsw_wallet.get_new_script(MIXDEPTH, True)
    change_amt = total_amt_in_sat - amount - FEE

    tx_outs = [
        {'script': binascii.hexlify(cj_script),
         'value': amount},
        {'script': binascii.hexlify(change_script),
         'value': change_amt}]
    tx = btc.deserialize(btc.mktx(tx_ins, tx_outs))
    binarize_tx(tx)

    # import new addresses to bitcoind
    jm_single().bc_interface.import_addresses(
        [nsw_wallet.script_to_addr(x)
         for x in [cj_script, change_script]],
        jm_single().bc_interface.get_wallet_name(nsw_wallet))

    # sign tx
    scripts = {}
    for nsw_in_index in o_ins:
        inp = nsw_ins[nsw_in_index][1]
        scripts[nsw_in_index] = (inp['script'], inp['value'])
    nsw_wallet.sign_tx(tx, scripts)

    scripts = {}
    for sw_in_index in segwit_ins:
        inp = sw_ins[sw_in_index][1]
        scripts[sw_in_index] = (inp['script'], inp['value'])
    sw_wallet.sign_tx(tx, scripts)

    print(tx)

    # push and verify
    txid = jm_single().bc_interface.pushtx(binascii.hexlify(btc.serialize(tx)))
    assert txid

    balances = jm_single().bc_interface.get_received_by_addr(
        [nsw_wallet.script_to_addr(cj_script),
         nsw_wallet.script_to_addr(change_script)], None)['data']
    assert balances[0]['balance'] == amount
    assert balances[1]['balance'] == change_amt
Esempio n. 6
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

    wallet_services = 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_service = wallet_services[num_ygs]['wallet']
    jmprint("\n\nTaker wallet seed : " + wallet_services[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: " + wallet_services[i]['seed'])
    jmprint("\n")
    wallet_service.sync_wallet(fast=True)
    ygclass = YieldGeneratorBasic

    # As per previous note, override non-default command line settings:
    options = {}
    for x in ["ordertype", "txfee", "txfee_factor", "cjfee_a", "cjfee_r",
              "cjfee_factor", "minsize", "size_factor"]:
        options[x] = jm_single().config.get("YIELDGENERATOR", x)
    ordertype = options["ordertype"]
    txfee = int(options["txfee"])
    txfee_factor = float(options["txfee_factor"])
    cjfee_factor = float(options["cjfee_factor"])
    size_factor = float(options["size_factor"])
    if ordertype == 'reloffer':
        cjfee_r = options["cjfee_r"]
        # 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)), int(options["minsize"]))
        cjfee_a = None
    elif ordertype == 'absoffer':
        cjfee_a = int(options["cjfee_a"])
        minsize = int(options["minsize"])
        cjfee_r = None
    else:
        assert False, "incorrect offertype config for yieldgenerator."

    txtype = wallet_service.get_txtype()
    if txtype == "p2wpkh":
        prefix = "sw0"
    elif txtype == "p2sh-p2wpkh":
        prefix = "sw"
    elif txtype == "p2pkh":
        prefix = ""
    else:
        assert False, "Unsupported wallet type for yieldgenerator: " + txtype

    ordertype = prefix + ordertype

    if malicious:
        if deterministic:
            ygclass = DeterministicMaliciousYieldGenerator
        else:
            ygclass = MaliciousYieldGenerator
    for i in range(num_ygs):
        
        cfg = [txfee, cjfee_a, cjfee_r, ordertype, minsize, txfee_factor,
               cjfee_factor, size_factor]
        wallet_service_yg = wallet_services[i]["wallet"]
        wallet_service_yg.startService()
        yg = ygclass(wallet_service_yg, 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)
def test_cj(setup_full_coinjoin, num_ygs, wallet_structures, mean_amt,
            malicious, deterministic):
    """Starts by setting up wallets for maker and taker bots; then,
    instantiates a single taker with the final wallet.
    The remaining wallets are used to set up YieldGenerators (basic form).
    All the wallets are given coins according to the rules of make_wallets,
    using the parameters for the values.
    The final start_reactor call is the only one that actually starts the
    reactor; the others only set up protocol instances.
    Inline are custom callbacks for the Taker, and these are basically
    copies of those in the `sendpayment.py` script for now, but they could
    be customized later for testing.
    The Taker's schedule is a single coinjoin, using basically random values,
    again this could be easily edited or parametrized if we feel like it.
    """

    # Set up some wallets, for the ygs and 1 sp.
    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']
    sync_wallet(wallet, fast=True)
    # grab a dest addr from the wallet
    destaddr = wallet.get_external_addr(4)
    coinjoin_amt = 20000000
    schedule = [[1, coinjoin_amt, 2, destaddr, 0.0, False]]
    """ The following two callback functions are as simple as possible
    modifications of the same in scripts/sendpayment.py
    """
    def filter_orders_callback(orders_fees, cjamount):
        return True

    def taker_finished(res, fromtx=False, waittime=0.0, txdetails=None):
        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

        if fromtx == "unconfirmed":
            #If final entry, stop *here*, don't wait for confirmation
            if taker.schedule_index + 1 == len(taker.schedule):
                reactor.stop()
                final_checks()
                return
        if fromtx:
            # currently this test uses a schedule with only one entry
            assert False, "taker_finished was called with fromtx=True"
            reactor.stop()
            return
        else:
            if not res:
                assert False, "Did not complete successfully, shutting down"
            # Note that this is required in both conditional branches,
            # especially in testing, because it's possible to receive the
            # confirmed callback before the unconfirmed.
            reactor.stop()
            final_checks()

    # twisted logging is required for debugging:
    startLogging(sys.stdout)

    taker = Taker(wallet,
                  schedule,
                  order_chooser=random_under_max_order_choose,
                  max_cj_fee=(0.1, 200),
                  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
    start_reactor(jm_single().config.get("DAEMON", "daemon_host"),
                  jm_single().config.getint("DAEMON", "daemon_port"),
                  clientfactory,
                  daemon=daemon,
                  rs=False)

    txfee = 1000
    cjfee_a = 4200
    cjfee_r = '0.001'
    ordertype = 'swreloffer'
    minsize = 100000
    ygclass = YieldGeneratorBasic
    # As noted above, this is not currently used but can be in future:
    if malicious or deterministic:
        raise NotImplementedError
    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
        # As noted above, only the final start_reactor() call will
        # actually start it!
        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)
def test_start_yg_and_taker_setup(setup_onion_ygrunner):
    """Set up some wallets, for the ygs and 1 taker.
    Then start LN and the ygs in the background, then fire
    a startup of a wallet daemon for the taker who then
    makes a coinjoin payment.
    """
    if jm_single().config.get("POLICY", "native") == "true":
        walletclass = SegwitWallet
    else:
        # TODO add Legacy
        walletclass = SegwitLegacyWallet

    start_bot_num, end_bot_num = [int(x) for x in jm_single().config.get(
        "MESSAGING:onion", "regtest_count").split(",")]
    num_ygs = end_bot_num - start_bot_num
    # specify the number of wallets and bots of each type:
    wallet_services = make_wallets(num_ygs + 1,
                           wallet_structures=[[1, 3, 0, 0, 0]] * (num_ygs + 1),
                           mean_amt=2.0,
                           walletclass=walletclass)
    #the sendpayment bot uses the last wallet in the list
    wallet_service = wallet_services[end_bot_num - 1]['wallet']
    jmprint("\n\nTaker wallet seed : " + wallet_services[end_bot_num - 1]['seed'])
    # for manual audit if necessary, show the maker's wallet seeds
    # also (note this audit should be automated in future)
    jmprint("\n\nMaker wallet seeds: ")
    for i in range(start_bot_num, end_bot_num):
        jmprint("Maker seed: " + wallet_services[i - 1]['seed'])
    jmprint("\n")
    wallet_service.sync_wallet(fast=True)
    ygclass = YieldGeneratorBasic

    # As per previous note, override non-default command line settings:
    options = {}
    for x in ["ordertype", "txfee_contribution", "txfee_contribution_factor",
              "cjfee_a", "cjfee_r", "cjfee_factor", "minsize", "size_factor"]:
        options[x] = jm_single().config.get("YIELDGENERATOR", x)
    ordertype = options["ordertype"]
    txfee_contribution = int(options["txfee_contribution"])
    txfee_contribution_factor = float(options["txfee_contribution_factor"])
    cjfee_factor = float(options["cjfee_factor"])
    size_factor = float(options["size_factor"])
    if ordertype == 'reloffer':
        cjfee_r = options["cjfee_r"]
        # minimum size is such that you always net profit at least 20%
        #of the miner fee
        minsize = max(int(1.2 * txfee_contribution / float(cjfee_r)),
            int(options["minsize"]))
        cjfee_a = None
    elif ordertype == 'absoffer':
        cjfee_a = int(options["cjfee_a"])
        minsize = int(options["minsize"])
        cjfee_r = None
    else:
        assert False, "incorrect offertype config for yieldgenerator."

    txtype = wallet_service.get_txtype()
    if txtype == "p2wpkh":
        prefix = "sw0"
    elif txtype == "p2sh-p2wpkh":
        prefix = "sw"
    elif txtype == "p2pkh":
        prefix = ""
    else:
        assert False, "Unsupported wallet type for yieldgenerator: " + txtype

    ordertype = prefix + ordertype

    for i in range(start_bot_num, end_bot_num):
        cfg = [txfee_contribution, cjfee_a, cjfee_r, ordertype, minsize,
               txfee_contribution_factor, cjfee_factor, size_factor]
        wallet_service_yg = wallet_services[i - 1]["wallet"]

        wallet_service_yg.startService()

        yg = ygclass(wallet_service_yg, cfg)
        clientfactory = RegtestJMClientProtocolFactory(yg, proto_type="MAKER")
        # This ensures that the right rpc/port config is passed into the daemon,
        # for this specific bot:
        clientfactory.i = i
        # This ensures that this bot knows which other bots are directory nodes:
        clientfactory.set_directory_nodes(directory_node_indices)
        nodaemon = jm_single().config.getint("DAEMON", "no_daemon")
        daemon = bool(nodaemon)
        #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=False)
    reactor.callLater(1.0, start_test_taker, wallet_services[end_bot_num - 1]['wallet'], end_bot_num, num_ygs)
    reactor.run()