Exemple #1
0
 def test_chain_params_context_manager(self):
     with ChainParams(BitcoinRegtestParams) as p1:
         assert isinstance(p1, BitcoinRegtestParams)
         with ChainParams(BitcoinSignetParams) as p2:
             assert isinstance(p2, BitcoinSignetParams)
             assert isinstance(get_current_chain_params(),
                               BitcoinSignetParams)
         assert isinstance(get_current_chain_params(), BitcoinRegtestParams)
Exemple #2
0
    def test_parse_config(self):
        conf_file_contents = """
            listen=1
            server=1

            rpcpassword=somepass # should be overriden

            regtest.rpcport = 8123

            rpcport = 8888

            [main]
            rpcuser=someuser1
            rpcpassword=somepass1
            rpcconnect=127.0.0.10

            [test]
            rpcpassword=somepass2
            rpcconnect=127.0.0.11
            rpcport = 9999

            [regtest]
            rpcuser=someuser3
            rpcpassword=somepass3
            rpcconnect=127.0.0.12
            """

        rpc = RPCCaller(conf_file_contents=conf_file_contents)
        self.assertEqual(rpc._RPCCaller__service_url, 'http://127.0.0.10:8888')
        authpair = "someuser1:somepass1"
        authhdr = "Basic " + base64.b64encode(
            authpair.encode('utf8')).decode('utf8')
        self.assertEqual(rpc._RPCCaller__auth_header, authhdr)

        with ChainParams('bitcoin/testnet'):
            rpc = RPCCaller(conf_file_contents=conf_file_contents)
            self.assertEqual(rpc._RPCCaller__service_url,
                             'http://127.0.0.11:9999')
            authpair = ":somepass2"  # no user specified
            authhdr = "Basic " + base64.b64encode(
                authpair.encode('utf8')).decode('utf8')
            self.assertEqual(rpc._RPCCaller__auth_header, authhdr)

        with ChainParams('bitcoin/regtest'):
            rpc = RPCCaller(conf_file_contents=conf_file_contents)
            self.assertEqual(rpc._RPCCaller__service_url,
                             'http://127.0.0.12:8123')
            authpair = "someuser3:somepass3"
            authhdr = "Basic " + base64.b64encode(
                authpair.encode('utf8')).decode('utf8')
            self.assertEqual(rpc._RPCCaller__auth_header, authhdr)
Exemple #3
0
    def test_MoneyRangeCustomParams(self):
        class CoreHighMaxClassDispatcher(CoreBitcoinClassDispatcher):
            ...

        class CoreHighMaxParams(CoreBitcoinParams, CoreBitcoinClass):
            @classgetter
            def MAX_MONEY(self):
                return 22000000 * self.COIN

        class WalletHighMaxClassDispatcher(WalletBitcoinClassDispatcher):
            ...

        class HighMaxParams(BitcoinMainnetParams):
            NAME = 'high_maxmoney'
            WALLET_DISPATCHER = WalletHighMaxClassDispatcher

        with ChainParams(HighMaxParams):
            self.assertFalse(MoneyRange(-1))
            with self.assertRaises(ValueError):
                coins_to_satoshi(-1)
            with self.assertRaises(ValueError):
                satoshi_to_coins(-1)
            self.assertTrue(MoneyRange(0))
            self.assertTrue(MoneyRange(100000))
            max_satoshi = coins_to_satoshi(22000000)
            self.assertTrue(
                MoneyRange(max_satoshi))  # Maximum money on Bitcoin network
            self.assertFalse(MoneyRange(max_satoshi + 1))
            with self.assertRaises(ValueError):
                coins_to_satoshi(max_satoshi + 1)
            with self.assertRaises(ValueError):
                satoshi_to_coins(max_satoshi + 1)
Exemple #4
0
    def test_legacy_p2sh(self):
        with ChainParams('litecoin', allow_legacy_p2sh=True):
            a = CCoinAddress('3F1c6UWAs9RLN2Mbt5bAJue12VhVCorXzs')
            self.assertIsInstance(a, P2SHLitecoinLegacyAddress)

        with ChainParams('litecoin'):
            with self.assertRaises(CCoinAddressError):
                a = CCoinAddress('3F1c6UWAs9RLN2Mbt5bAJue12VhVCorXzs')

        l_addr = '3N4DqfrHhStCao4NjwroxoegjydkJk3P9Z'
        c_addr = 'MUGN9ZGFeZjdPJLGqpr9nSu64gECLRzQrx'
        self.assertEqual(
            str(
                P2SHLitecoinAddress.from_scriptPubKey(
                    P2SHLitecoinLegacyAddress(l_addr).to_scriptPubKey())),
            c_addr)
Exemple #5
0
def test_valid_bip341_scriptpubkeys_addresses():
    with ChainParams("bitcoin"):
        with open(os.path.join(testdir, "bip341_wallet_test_vectors.json"),
                  "r") as f:
            json_data = json.loads(f.read())
        for x in json_data["scriptPubKey"]:
            sPK = hextobin(x["expected"]["scriptPubKey"])
            addr = x["expected"]["bip350Address"]
            res, message = validate_address(addr)
            assert res, message
            print("address {} was valid bech32m".format(addr))
            # test this specific conversion because this is how
            # our human readable outputs work:
            assert str(CCoinAddress.from_scriptPubKey(
                btc.CScript(sPK))) == addr
            print("and it converts correctly from scriptPubKey: {}".format(
                btc.CScript(sPK)))
Exemple #6
0
def test_address_implementations(test,
                                 paramclasses=None,
                                 extra_addr_testfunc=lambda *args: False):
    pub = CPubKey(
        x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71')
    )
    if paramclasses is None:
        paramclasses = bitcointx.get_registered_chain_params()
    for paramclass in paramclasses:
        with ChainParams(paramclass):

            def recursive_check(aclass):
                test.assertTrue(issubclass(aclass, CCoinAddress))

                if extra_addr_testfunc(aclass, pub):
                    pass
                else:
                    a = None

                    if getattr(aclass, 'from_pubkey', None):
                        a = aclass.from_pubkey(pub)
                    elif getattr(aclass, 'from_redeemScript', None):
                        a = aclass.from_redeemScript(
                            CScript(b'\xa9' + Hash160(pub) + b'\x87'))
                    else:
                        assert len(dispatcher_mapped_list(aclass)) > 0,\
                            ("dispatcher mapped list for {} "
                                "must not be empty".format(aclass))

                    if a is not None:
                        spk = a.to_scriptPubKey()
                        test.assertEqual(a, aclass.from_scriptPubKey(spk))
                        a2 = aclass.from_bytes(a)
                        test.assertEqual(bytes(a), bytes(a2))
                        test.assertEqual(str(a), str(a2))
                        a3 = aclass(str(a))
                        test.assertEqual(bytes(a), bytes(a3))
                        test.assertEqual(str(a), str(a3))

                for next_aclass in dispatcher_mapped_list(aclass):
                    recursive_check(next_aclass)

            recursive_check(CCoinAddress)
Exemple #7
0
    def test_MoneyRangeCustomParams(self) -> None:
        class CoreHighMaxClassDispatcher(CoreBitcoinClassDispatcher):
            ...

        class CoreHighMaxClass(CoreBitcoinClass,
                               metaclass=CoreHighMaxClassDispatcher):
            ...

        class CoreHighMaxParams(CoreBitcoinParams, CoreHighMaxClass):
            @classgetter
            def MAX_MONEY(self) -> int:
                return 10**100 * self.COIN

        class WalletHighMaxClassDispatcher(WalletBitcoinClassDispatcher,
                                           depends=[
                                               CoreHighMaxClassDispatcher
                                           ]):
            ...

        class HighMaxParams(BitcoinMainnetParams):
            NAME = 'high_maxmoney'
            WALLET_DISPATCHER = WalletHighMaxClassDispatcher

        with self.assertRaises(ValueError):
            coins_to_satoshi(10**100)

        with ChainParams(HighMaxParams):
            self.assertFalse(MoneyRange(-1))
            with self.assertRaises(ValueError):
                coins_to_satoshi(-1)
            with self.assertRaises(ValueError):
                satoshi_to_coins(-1)
            self.assertTrue(MoneyRange(0))
            self.assertTrue(MoneyRange(100000))
            max_satoshi = coins_to_satoshi(10**100)
            self.assertEqual(satoshi_to_coins(max_satoshi), 10**100)
            self.assertTrue(MoneyRange(max_satoshi))
            self.assertFalse(MoneyRange(max_satoshi + 1))
            with self.assertRaises(ValueError):
                coins_to_satoshi(max_satoshi + 1)
            with self.assertRaises(ValueError):
                satoshi_to_coins(max_satoshi + 1)
Exemple #8
0
    def test_get_output_size(self):
        pub1 = CPubKey(
            x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71'
              ))
        pub2 = CPubKey(
            x('02546c76587482cd2468b76768da70c0166ecb2aa2eb1038624f4fedc138b042bc'
              ))
        for chainparam in get_params_list():
            with ChainParams(chainparam):
                smpl = get_unconfidential_address_samples(pub1, pub2)
                # 1 byte for 'no asset', 1 byte for 'no nonce',
                # 9 bytes for explicit value,
                # minus 8 bytes of len of bitcoin nValue
                elements_unconfidential_size_extra = 1 + 1 + 9 - 8

                # 33 bytes for asset, 33 bytes for nonce,
                # 33 bytes for confidential value,
                # minus 8 bytes of len of bitcoin nValue
                elements_confidential_size_extra = 33 + 33 + 33 - 8

                self.assertEqual(smpl.p2pkh.get_output_size(),
                                 34 + elements_unconfidential_size_extra)
                self.assertEqual(smpl.p2wpkh.get_output_size(),
                                 31 + elements_unconfidential_size_extra)
                self.assertEqual(smpl.p2sh.get_output_size(),
                                 32 + elements_unconfidential_size_extra)
                self.assertEqual(smpl.p2wsh.get_output_size(),
                                 43 + elements_unconfidential_size_extra)
                self.assertEqual(smpl.conf_p2pkh.get_output_size(),
                                 34 + elements_confidential_size_extra)
                self.assertEqual(smpl.conf_p2wpkh.get_output_size(),
                                 31 + elements_confidential_size_extra)
                self.assertEqual(smpl.conf_p2sh.get_output_size(),
                                 32 + elements_confidential_size_extra)
                self.assertEqual(smpl.conf_p2wsh.get_output_size(),
                                 43 + elements_confidential_size_extra)
Exemple #9
0
    def test_from_to_unconfidential(self):  #noqa
        pub1 = CPubKey(
            x('02546c76587482cd2468b76768da70c0166ecb2aa2eb1038624f4fedc138b042bc'
              ))
        pub2 = CPubKey(
            x('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71'
              ))
        params_list = get_params_list()
        for pl_index, chainparam in enumerate(params_list):
            next_chainparam = (params_list[0] if pl_index + 1
                               == len(params_list) else params_list[pl_index +
                                                                    1])
            with ChainParams(chainparam):
                mapped_cls_list = dispatcher_mapped_list(
                    CCoinConfidentialAddress)
                assert len(mapped_cls_list) == 1
                chain_specific_cls = mapped_cls_list[0]
                with ChainParams(next_chainparam):
                    mapped_cls_list = dispatcher_mapped_list(
                        CCoinConfidentialAddress)
                    assert len(mapped_cls_list) == 1
                    next_chain_specific_cls = mapped_cls_list[0]
                    assert next_chain_specific_cls is not chain_specific_cls
                smpl = get_unconfidential_address_samples(pub1, pub2)
                for uct in unconf_types:
                    for ct in conf_types:
                        unconf = getattr(smpl, uct)
                        conf = getattr(smpl, ct)
                        with self.assertRaises(TypeError):
                            next_chain_specific_cls.from_unconfidential(
                                unconf, pub2)

                        if ct.endswith(uct):
                            self.assertEqual(str(conf.to_unconfidential()),
                                             str(unconf))
                            self.assertEqual(
                                str(conf.from_unconfidential(unconf, pub2)),
                                str(conf))
                            self.assertNotEqual(
                                str(conf.from_unconfidential(unconf, pub1)),
                                str(conf))
                            self.assertEqual(
                                str(
                                    CCoinConfidentialAddress.
                                    from_unconfidential(unconf, pub2)),
                                str(conf))
                            self.assertEqual(
                                str(
                                    chain_specific_cls.from_unconfidential(
                                        unconf, pub2)), str(conf))
                            if ct.endswith('p2pkh'):
                                self.assertEqual(
                                    str(
                                        CBase58CoinConfidentialAddress.
                                        from_unconfidential(unconf, pub2)),
                                    str(conf))
                                self.assertEqual(
                                    str(
                                        P2PKHCoinConfidentialAddress.
                                        from_unconfidential(unconf, pub2)),
                                    str(conf))
                            elif ct.endswith('p2sh'):
                                self.assertEqual(
                                    str(
                                        CBase58CoinConfidentialAddress.
                                        from_unconfidential(unconf, pub2)),
                                    str(conf))
                                self.assertEqual(
                                    str(
                                        P2SHCoinConfidentialAddress.
                                        from_unconfidential(unconf, pub2)),
                                    str(conf))
                            elif ct.endswith('p2wpkh'):
                                self.assertEqual(
                                    str(
                                        CBlech32CoinConfidentialAddress.
                                        from_unconfidential(unconf, pub2)),
                                    str(conf))
                                self.assertEqual(
                                    str(
                                        P2WPKHCoinConfidentialAddress.
                                        from_unconfidential(unconf, pub2)),
                                    str(conf))
                            elif ct.endswith('p2wsh'):
                                self.assertEqual(
                                    str(
                                        CBlech32CoinConfidentialAddress.
                                        from_unconfidential(unconf, pub2)),
                                    str(conf))
                                self.assertEqual(
                                    str(
                                        P2WSHCoinConfidentialAddress.
                                        from_unconfidential(unconf, pub2)),
                                    str(conf))
                            else:
                                assert 0, "unexpected addr type"

                            if issubclass(conf.__class__,
                                          CBlech32CoinConfidentialAddress):
                                with self.assertRaises(
                                        CConfidentialAddressError):
                                    CBase58CoinConfidentialAddress.from_unconfidential(
                                        unconf, pub2)
                            elif issubclass(conf.__class__,
                                            CBase58CoinConfidentialAddress):
                                with self.assertRaises(
                                        CConfidentialAddressError):
                                    CBlech32CoinConfidentialAddress.from_unconfidential(
                                        unconf, pub2)
                            else:
                                assert 0, "unexpected conf.__class__"

                            for ct2 in conf_types:
                                if ct != ct2:
                                    conf_cls = getattr(smpl, ct2).__class__
                                    with self.assertRaises(TypeError):
                                        conf_cls.from_unconfidential(
                                            unconf, pub2)
                        else:
                            self.assertNotEqual(str(conf.to_unconfidential()),
                                                str(unconf))
                            with self.assertRaises(TypeError):
                                conf.from_unconfidential(unconf, pub2)
Exemple #10
0
def participant(func, name, pipe, bitcoin_config_path, elements_config_path):
    """Prepares environment for participants, run their functions,
    and handles the errors they did not bother to hanlde"""
    def say(msg):
        participant_says(name, msg)

    # Custom exception class to distinguish a case when
    # participant calss die() from other exceptions
    class ProtocolFailure(Exception):
        ...

    def do_last_wish(msg):
        global last_wish_func
        lwf = last_wish_func
        if lwf:
            say("Going to die because '{}', "
                "but I still have something to do before that.".format(msg))
            last_wish_func = None
            lwf()

    def die(msg, peacefully=False):
        do_last_wish(msg)
        if peacefully:
            sys.exit(-1)
        raise ProtocolFailure(msg)

    def recv(expected_type, timeout=60):
        if not pipe.poll(timeout):
            die('No messages received in {} seconds'.format(timeout))
        msg = pipe.recv()

        if msg[0] == 'bye!':
            msg = 'Communication finished unexpectedly'
            say(msg + ', exiting.')
            do_last_wish(msg)
            sys.exit(-1)

        if msg[0] != expected_type:
            die("unexpected message type '{}', expected '{}'".format(
                msg[0], expected_type))

        return msg[1]

    def send(msg_type, data=None):
        pipe.send([msg_type, data])

    # Ignore keyboard interrupt, parent process handles it.
    signal.signal(signal.SIGINT, signal.SIG_IGN)

    try:
        # Connect to Elements or Bitcoin RPC with specified path
        # Do it with context params switch, so that appropriate values
        # from config files will be used (there may be regtest.port=N, etc)
        with ChainParams(bitcoin_chain_name):
            btc_rpc = connect_rpc(say, bitcoin_config_path)
        with ChainParams(elements_chain_name):
            elt_rpc = connect_rpc(say, elements_config_path)
        # Execute participant's function
        func(say, recv, send, die, btc_rpc, elt_rpc)
    except Exception as e:
        say('FAIL with {}: {}'.format(type(e).__name__, e))
        say("Traceback:")
        print("=" * 80)
        traceback.print_tb(sys.exc_info()[-1])
        print("=" * 80)
        do_last_wish(e.__class__.__name__)
        send('bye!')
        sys.exit(-1)
Exemple #11
0
def bob(say, recv, send, die, btc_rpc, elt_rpc):
    """A function that implements the logic
    of the Bitcoin-side participant
    of confidential cross-chain atomic swap"""

    global last_wish_func

    # Default chain for Bob will be Bitcoin
    # To handle bitcoin-related objects, either
    # `with ChainParams(elements_chain_name):` have to be used, or
    # concrete classes, like CElementsAddress, CElementsTransaction, etc.
    select_chain_params(bitcoin_chain_name)

    say('Waiting for blinding key from Alice')
    alice_btc_pub_raw, alice_elt_exit_pub_raw = recv('pubkeys')

    blinding_key = CKey.from_secret_bytes(recv('blinding_key'))
    say("Pubkey for blinding key: {}".format(b2x(blinding_key.pub)))

    # Let's create the key that would lock the coins on Bitcoin side
    contract_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for Elements side
    bob_elt_spend_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for 'timeout' case on btc side
    bob_btc_exit_key = CKey.from_secret_bytes(os.urandom(32))

    key_to_reveal_pub = CPubKey.add(contract_key.pub, blinding_key.pub)
    say("The pubkey of the combined key to be revealed: {}".format(
        b2x(key_to_reveal_pub)))

    say('Sending my pubkeys to Alice')
    send('pubkeys',
         (contract_key.pub, bob_elt_spend_key.pub, bob_btc_exit_key.pub))

    combined_btc_spend_pubkey = CPubKey.add(contract_key.pub,
                                            CPubKey(alice_btc_pub_raw))

    say('combined_btc_spend_pubkey: {}'.format(b2x(combined_btc_spend_pubkey)))
    btc_contract = make_btc_contract(combined_btc_spend_pubkey,
                                     bob_btc_exit_key.pub)

    btc_contract_addr = P2WSHCoinAddress.from_redeemScript(btc_contract)

    say("Created Bitcoin-side swap contract, size: {}".format(
        len(btc_contract)))
    say("Contract address: {}".format(btc_contract_addr))

    say('Sending {} to {}'.format(pre_agreed_amount, btc_contract_addr))
    btc_txid = btc_rpc.sendtoaddress(str(btc_contract_addr), pre_agreed_amount)

    def bob_last_wish_func():
        try_reclaim_btc(say, btc_rpc, btc_txid, btc_contract, bob_btc_exit_key,
                        die)

    last_wish_func = bob_last_wish_func

    wait_confirm(say, 'Bitcoin', btc_txid, die, btc_rpc, num_confirms=6)

    send('btc_txid', btc_txid)
    elt_txid = recv('elt_txid')

    elt_contract = make_elt_cntract(key_to_reveal_pub, bob_elt_spend_key.pub,
                                    alice_elt_exit_pub_raw)

    with ChainParams(elements_chain_name):
        elt_contract_addr = P2SHCoinAddress.from_redeemScript(elt_contract)

    say('Got Elements contract address from Alice: {}'.format(
        elt_contract_addr))
    say('Looking for this address in transaction {} in Elements'.format(
        elt_txid))

    tx_json = elt_rpc.getrawtransaction(elt_txid, 1)

    if tx_json['confirmations'] < 2:
        die('Transaction does not have enough confirmations')

    elt_commit_tx = CElementsTransaction.deserialize(x(tx_json['hex']))

    vout_n, unblind_result = find_and_unblind_vout(say, elt_commit_tx,
                                                   elt_contract_addr,
                                                   blinding_key, die)

    if unblind_result.amount != coins_to_satoshi(pre_agreed_amount):
        die('the amount {} found at the output in the offered transaction '
            'does not match the expected amount {}'.format(
                satoshi_to_coins(unblind_result.amount), pre_agreed_amount))

    say('The asset and amount match expected values. lets spend it.')

    with ChainParams(elements_chain_name):
        dst_addr = CCoinAddress(elt_rpc.getnewaddress())
        assert isinstance(dst_addr, CCoinConfidentialAddress)

        say('I will claim my Elements-BTC to {}'.format(dst_addr))

        elt_claim_tx = create_elt_spend_tx(
            dst_addr,
            elt_txid,
            vout_n,
            elt_contract,
            die,
            spend_key=bob_elt_spend_key,
            contract_key=contract_key,
            blinding_key=blinding_key,
            blinding_factor=unblind_result.blinding_factor,
            asset_blinding_factor=unblind_result.asset_blinding_factor)

        # Cannot use VerifyScript for now,
        # because it does not support CHECKSIGFROMSTACK yet
        #
        # VerifyScript(tx.vin[0].scriptSig,
        #              elt_contract_addr.to_scriptPubKey(),
        #              tx, 0, amount=amount)

    say('Sending my spend-reveal transaction')
    sr_txid = elt_rpc.sendrawtransaction(b2x(elt_claim_tx.serialize()))

    wait_confirm(say, 'Elements', sr_txid, die, elt_rpc, num_confirms=2)

    say('Got my Elements-BTC. Swap successful (at least for me :-)')
Exemple #12
0
def alice(say, recv, send, die, btc_rpc, elt_rpc):
    """A function that implements the logic
    of the Elements-side participant
    of confidential cross-chain atomic swap"""

    global last_wish_func

    # Default chain for Alice will be Elements
    # To handle bitcoin-related objects, either
    # `with ChainParams(bitcoin_chain_name):` have to be used, or
    # concrete classes, like CBitcoinAddress, CBitcoinTransaction, etc.
    select_chain_params(elements_chain_name)

    # Let's create the shared blinding key
    blinding_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for btc spend
    alice_btc_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for the 'timeout' branch of the contract
    alice_elt_exit_key = CKey.from_secret_bytes(os.urandom(32))

    say('Sending pubkeys to Bob')
    send('pubkeys', (alice_btc_key.pub, alice_elt_exit_key.pub))

    say('Sending the blinding key to Bob')
    send('blinding_key', blinding_key.secret_bytes)

    (contract_pubkey_raw, bob_elt_pubkey_raw,
     bob_btc_exit_pub_raw) = recv('pubkeys')

    say("Pubkey of the key to be revealed: {}".format(
        b2x(contract_pubkey_raw)))
    say("Bob's Elements-side pubkey: {}".format(b2x(bob_elt_pubkey_raw)))

    contract_pubkey = CPubKey(contract_pubkey_raw)

    key_to_reveal_pub = CPubKey.add(contract_pubkey, blinding_key.pub)

    elt_contract = make_elt_cntract(key_to_reveal_pub, bob_elt_pubkey_raw,
                                    alice_elt_exit_key.pub)

    elt_contract_addr = P2SHCoinAddress.from_redeemScript(elt_contract)

    confidential_contract_addr = P2SHCoinConfidentialAddress.from_unconfidential(
        elt_contract_addr, blinding_key.pub)
    assert isinstance(confidential_contract_addr, CElementsConfidentialAddress)

    say("Created Elemets-side swap contract, size: {}".format(
        len(elt_contract)))
    say("Contract address:\n\tconfidential: {}\n\tunconfidential: {}".format(
        confidential_contract_addr, elt_contract_addr))

    btc_txid = recv('btc_txid')

    combined_btc_spend_pubkey = CPubKey.add(contract_pubkey, alice_btc_key.pub)
    btc_contract = make_btc_contract(combined_btc_spend_pubkey,
                                     bob_btc_exit_pub_raw)

    tx_json = btc_rpc.getrawtransaction(btc_txid, 1)

    if tx_json['confirmations'] < 6:
        die('Transaction does not have enough confirmations')

    # We use ChainParams, and not P2WSHBitcoinAddress here,
    # because bitcoin_chain_name might be 'bitcoin/regtest', for example,
    # and then the address would need to be P2WSHBitcoinRegtestAddress.
    # with ChainParams we leverage the 'frontend class' magic, P2WSHCoinAddress
    # will give us appropriate instance.
    with ChainParams(bitcoin_chain_name):
        btc_contract_addr = P2WSHCoinAddress.from_redeemScript(btc_contract)
        say('Looking for this address in transaction {} in Bitcoin'.format(
            btc_txid))

    # CTransaction subclasses do not change between mainnet/testnet/regtest,
    # so we can directly use CBitcoinTransaction.
    # That might not be true for other chains, though.
    # You might also want to use CTransaction within `with ChainParams(...):`
    btc_tx = CBitcoinTransaction.deserialize(x(tx_json['hex']))

    for n, vout in enumerate(btc_tx.vout):
        if vout.scriptPubKey == btc_contract_addr.to_scriptPubKey():
            say("Found the address at output {}".format(n))
            btc_vout_n = n
            break
    else:
        die('Did not find contract address in transaction')

    if vout.nValue != coins_to_satoshi(pre_agreed_amount):
        die('the amount {} found at the output in the offered transaction '
            'does not match the expected amount {}'.format(
                satoshi_to_coins(vout.nValue), pre_agreed_amount))

    say('Bitcoin amount match expected values')

    say('Sending {} to {}'.format(pre_agreed_amount,
                                  confidential_contract_addr))
    contract_txid = elt_rpc.sendtoaddress(str(confidential_contract_addr),
                                          pre_agreed_amount)

    def alice_last_wish_func():
        try_reclaim_elt(say, elt_rpc, contract_txid, elt_contract,
                        alice_elt_exit_key, blinding_key, die)

    last_wish_func = alice_last_wish_func

    wait_confirm(say, 'Elements', contract_txid, die, elt_rpc, num_confirms=2)

    send('elt_txid', contract_txid)

    sr_txid = wait_spend_reveal_transaction(say, contract_txid, die, elt_rpc)

    say('Got txid for spend-reveal transaction from Bob ({})'.format(sr_txid))

    tx_json = elt_rpc.getrawtransaction(sr_txid, 1)

    wait_confirm(say, 'Elements', sr_txid, die, elt_rpc, num_confirms=2)

    sr_tx = CTransaction.deserialize(x(tx_json['hex']))

    for n, vin in enumerate(sr_tx.vin):
        if vin.prevout.hash == lx(contract_txid)\
                and vin.scriptSig[-(len(elt_contract)):] == elt_contract:
            say('Transaction input {} seems to contain a script '
                'we can recover the key from'.format(n))
            reveal_script_iter = iter(vin.scriptSig)
            break
    else:
        die('Spend-reveal transaction does not have input that spends '
            'the contract output')

    next(reveal_script_iter)  # skip Bob's spend signature

    try:
        # 2 skipped bytes are tag and len
        sig_s = ecdsa.util.string_to_number(next(reveal_script_iter)[2:])
    except (ValueError, StopIteration):
        die('Reveal script is invalid')

    k, r = get_known_k_r()
    order = ecdsa.SECP256k1.order
    mhash = ecdsa.util.string_to_number(hashlib.sha256(b'\x01').digest())
    r_inverse = ecdsa.numbertheory.inverse_mod(r, order)

    for s in (-sig_s, sig_s):
        secret_exponent = (((s * k - mhash) % order) * r_inverse) % order

        recovered_key = CKey.from_secret_bytes(
            ecdsa.util.number_to_string(secret_exponent, order))

        if recovered_key.pub == key_to_reveal_pub:
            break
    else:
        die('Key recovery failed. Should not happen - the sig was already '
            'verified when transaction was accepted into mempool. '
            'Must be a bug.')

    say('recovered key pubkey: {}'.format(b2x(recovered_key.pub)))
    contract_key = CKey.sub(recovered_key, blinding_key)
    say('recovered unblined key pubkey: {}'.format(b2x(contract_key.pub)))
    combined_btc_spend_key = CKey.add(contract_key, alice_btc_key)

    say('Successfully recovered the key. Can now spend Bitcoin from {}'.format(
        btc_contract_addr))

    with ChainParams(bitcoin_chain_name):
        dst_addr = CCoinAddress(btc_rpc.getnewaddress())
        btc_claim_tx = create_btc_spend_tx(dst_addr,
                                           btc_txid,
                                           btc_vout_n,
                                           btc_contract,
                                           spend_key=combined_btc_spend_key)

    say('Sending my Bitcoin-claim transaction')
    btc_claim_txid = btc_rpc.sendrawtransaction(b2x(btc_claim_tx.serialize()))

    wait_confirm(say, 'Bitcoin', btc_claim_txid, die, btc_rpc, num_confirms=3)

    say('Got my Bitcoin. Swap successful!')