예제 #1
0
    def test_parse_standard_multisig_redeem_script(self):
        def T(script):
            return parse_standard_multisig_redeem_script(script)

        def T2(script, result):
            info = T(script)
            self.assertEqual(info.total, result.total)
            self.assertEqual(info.required, result.required)
            self.assertEqual(info.pubkeys, result.pubkeys)

        # NOTE: p2sh_multisig_parse_script does not check validity of pubkeys
        pubkeys = [CKey.from_secret_bytes(os.urandom(32)).pub
                   for _ in range(15)]

        T2(CScript([1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]),
           StandardMultisigScriptInfo(total=2, required=1,
                                      pubkeys=pubkeys[:2]))
        T2(CScript([11] + pubkeys[:12] + [12, OP_CHECKMULTISIG]),
           StandardMultisigScriptInfo(total=12, required=11,
                                      pubkeys=pubkeys[:12]))
        T2(CScript([15] + pubkeys + [15, OP_CHECKMULTISIG]),
           StandardMultisigScriptInfo(total=15, required=15, pubkeys=pubkeys))

        with self.assertRaises(ValueError):
            # invalid script - extra opcode
            T(CScript([1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG, OP_DROP]))
        with self.assertRaises(ValueError):
            # invalid pubkey - extra data
            T(CScript([1, pubkeys[0]+b'abc', pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            # invalid pubkey - truncated
            T(CScript([1, pubkeys[0], pubkeys[1][:-1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([]))
        with self.assertRaises(ValueError):
            T(CScript([b'\x01', pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([16, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [b'\x0c', OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys + [14, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys + pubkeys + [15, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([1, pubkeys[0], pubkeys[1], 1, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11, OP_CHECKSIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12]))  # short script 1
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11]))  # short script 2
예제 #2
0
    def test_parse_standard_multisig_redeem_script(self):
        def T(script, result):
            self.assertEqual(parse_standard_multisig_redeem_script(script),
                             result)

        # NOTE: p2sh_multisig_parse_script does not check validity of pubkeys
        pubkeys = [
            CKey.from_secret_bytes(os.urandom(32)).pub for _ in range(15)
        ]

        T(CScript([1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]), {
            'total': 2,
            'required': 1,
            'pubkeys': pubkeys[:2]
        })
        T(CScript([11] + pubkeys[:12] + [12, OP_CHECKMULTISIG]), {
            'total': 12,
            'required': 11,
            'pubkeys': pubkeys[:12]
        })
        T(CScript([15] + pubkeys + [15, OP_CHECKMULTISIG]), {
            'total': 15,
            'required': 15,
            'pubkeys': pubkeys
        })

        with self.assertRaises(ValueError):
            T(CScript([]), {})
        with self.assertRaises(ValueError):
            T(CScript([b'\x01', pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]),
              {})
        with self.assertRaises(ValueError):
            T(CScript([16, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [b'\x0c', OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys + [14, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys + pubkeys + [15, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([1, pubkeys[0], pubkeys[1], 1, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11, OP_CHECKSIG]), {})
        with self.assertRaises(ValueError):
            T(
                CScript([11] + pubkeys[:12]),  # short script 1
                {})
        with self.assertRaises(ValueError):
            T(
                CScript([11] + pubkeys[:12] + [11]),  # short script 2
                {})
예제 #3
0
    def test_sighash(self) -> None:
        spent_amount = 1100
        pub = CKey.from_secret_bytes(os.urandom(32)).pub
        spk_legacy = P2PKHCoinAddress.from_pubkey(pub).to_scriptPubKey()
        spk_segwit = P2WPKHCoinAddress.from_pubkey(pub).to_scriptPubKey()

        tx = CTransaction([
            CTxIn(
                COutPoint(b'\x00' * 32, 0), CScript([]), nSequence=0xFFFFFFFF)
        ], [CTxOut(1000, spk_legacy)],
                          nLockTime=0,
                          nVersion=1)

        # no exceptions should be raised with these two calls
        spk_legacy.sighash(tx,
                           0,
                           SIGHASH_ALL,
                           amount=spent_amount,
                           sigversion=SIGVERSION_WITNESS_V0)
        spk_segwit.sighash(tx,
                           0,
                           SIGHASH_ALL,
                           amount=spent_amount,
                           sigversion=SIGVERSION_WITNESS_V0)

        with self.assertRaises(ValueError):
            # unknown sigversion
            spk_segwit.sighash(tx,
                               0,
                               SIGHASH_ALL,
                               amount=spent_amount,
                               sigversion=SIGVERSION_WITNESS_V0 +
                               1)  # type: ignore

        assert spk_segwit.is_witness_scriptpubkey()
        with self.assertRaises(ValueError):
            # incorect sigversion for segwit
            spk_segwit.sighash(tx,
                               0,
                               SIGHASH_ALL,
                               amount=spent_amount,
                               sigversion=SIGVERSION_BASE)

        with self.assertRaises(ValueError):
            # inIdx > len(tx.vin) - non-raw sighash function should raise
            # ValueError (raw_sighash can return HASH_ONE)
            spk_legacy.sighash(tx,
                               10,
                               SIGHASH_ALL,
                               amount=spent_amount,
                               sigversion=SIGVERSION_BASE)
예제 #4
0
        def T(required: int,
              total: int,
              alt_total: Optional[int] = None) -> None:
            amount = 10000
            keys = [
                CKey.from_secret_bytes(os.urandom(32)) for _ in range(total)
            ]
            pubkeys = [k.pub for k in keys]

            if alt_total is not None:
                total = alt_total  # for assertRaises checks

            redeem_script = standard_multisig_redeem_script(total=total,
                                                            required=required,
                                                            pubkeys=pubkeys)

            # Test with P2SH

            scriptPubKey = redeem_script.to_p2sh_scriptPubKey()

            (_, tx) = self.create_test_txs(CScript(), scriptPubKey,
                                           CScriptWitness([]), amount)

            tx = tx.to_mutable()

            sighash = redeem_script.sighash(tx,
                                            0,
                                            SIGHASH_ALL,
                                            amount=amount,
                                            sigversion=SIGVERSION_BASE)

            sigs = [
                k.sign(sighash) + bytes([SIGHASH_ALL]) for k in keys[:required]
            ]

            tx.vin[0].scriptSig = CScript(
                standard_multisig_witness_stack(sigs, redeem_script))

            VerifyScript(tx.vin[0].scriptSig, scriptPubKey, tx, 0,
                         (SCRIPT_VERIFY_P2SH, ))

            # Test with P2WSH

            scriptPubKey = redeem_script.to_p2wsh_scriptPubKey()

            (_, tx) = self.create_test_txs(CScript(), scriptPubKey,
                                           CScriptWitness([]), amount)

            tx = tx.to_mutable()

            sighash = redeem_script.sighash(tx,
                                            0,
                                            SIGHASH_ALL,
                                            amount=amount,
                                            sigversion=SIGVERSION_WITNESS_V0)

            sigs = [
                k.sign(sighash) + bytes([SIGHASH_ALL]) for k in keys[:required]
            ]

            witness_stack = standard_multisig_witness_stack(
                sigs, redeem_script)
            tx.vin[0].scriptSig = CScript([])
            tx.wit.vtxinwit[0] = CTxInWitness(
                CScriptWitness(witness_stack)).to_mutable()

            VerifyScript(tx.vin[0].scriptSig,
                         scriptPubKey,
                         tx,
                         0,
                         flags=(SCRIPT_VERIFY_WITNESS, SCRIPT_VERIFY_P2SH),
                         amount=amount,
                         witness=tx.wit.vtxinwit[0].scriptWitness)

            # Test with P2SH_P2WSH

            scriptPubKey = redeem_script.to_p2wsh_scriptPubKey()

            (_, tx) = self.create_test_txs(CScript(), scriptPubKey,
                                           CScriptWitness([]), amount)

            tx = tx.to_mutable()

            sighash = redeem_script.sighash(tx,
                                            0,
                                            SIGHASH_ALL,
                                            amount=amount,
                                            sigversion=SIGVERSION_WITNESS_V0)

            sigs = [
                k.sign(sighash) + bytes([SIGHASH_ALL]) for k in keys[:required]
            ]

            witness_stack = standard_multisig_witness_stack(
                sigs, redeem_script)
            tx.vin[0].scriptSig = CScript([scriptPubKey])
            tx.wit.vtxinwit[0] = CTxInWitness(
                CScriptWitness(witness_stack)).to_mutable()

            VerifyScript(tx.vin[0].scriptSig,
                         scriptPubKey.to_p2sh_scriptPubKey(),
                         tx,
                         0,
                         flags=(SCRIPT_VERIFY_WITNESS, SCRIPT_VERIFY_P2SH),
                         amount=amount,
                         witness=tx.wit.vtxinwit[0].scriptWitness)
예제 #5
0
    def test_parse_standard_multisig_redeem_script(self) -> None:
        def T(script: CScript) -> StandardMultisigScriptInfo:
            return parse_standard_multisig_redeem_script(script)

        def T2(script: CScript, result: StandardMultisigScriptInfo) -> None:
            info = T(script)
            self.assertEqual(info.total, result.total)
            self.assertEqual(info.required, result.required)
            self.assertEqual(info.pubkeys, result.pubkeys)

        # NOTE: p2sh_multisig_parse_script does not check validity of pubkeys
        pubkeys = [
            CKey.from_secret_bytes(os.urandom(32)).pub for _ in range(15)
        ]

        T2(
            CScript([1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]),
            StandardMultisigScriptInfo(total=2,
                                       required=1,
                                       pubkeys=pubkeys[:2]))

        # "list-any" - to workaround to
        # 'Unsupported operand types for + (List[int] and List[CPubKey])
        la: List[Any] = []

        T2(
            CScript(la + [11] + pubkeys[:12] + [12, OP_CHECKMULTISIG]),
            StandardMultisigScriptInfo(total=12,
                                       required=11,
                                       pubkeys=pubkeys[:12]))
        T2(CScript(la + [15] + pubkeys + [15, OP_CHECKMULTISIG]),
           StandardMultisigScriptInfo(total=15, required=15, pubkeys=pubkeys))

        with self.assertRaises(ValueError):
            # invalid script - extra opcode
            T(
                CScript(
                    [1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG, OP_DROP]))
        with self.assertRaises(ValueError):
            # invalid pubkey - extra data
            T(
                CScript(
                    [1, pubkeys[0] + b'abc', pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            # invalid pubkey - truncated
            T(CScript([1, pubkeys[0], pubkeys[1][:-1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([]))
        with self.assertRaises(ValueError):
            T(CScript([b'\x01', pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([16, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12] + [b'\x0c', OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys + [14, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys + pubkeys + [15, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([1, pubkeys[0], pubkeys[1], 1, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12] + [11, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12] + [11, OP_CHECKSIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12]))  # short script 1
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12] + [11]))  # short script 2
예제 #6
0
def create_elt_spend_tx(dst_addr,
                        txid,
                        vout_n,
                        elt_contract,
                        die,
                        spend_key=None,
                        contract_key=None,
                        blinding_key=None,
                        blinding_factor=None,
                        asset_blinding_factor=None,
                        branch_condition=True):

    fee_satoshi = coins_to_satoshi(fixed_fee_amount)
    out_amount = coins_to_satoshi(pre_agreed_amount) - fee_satoshi

    # Single blinded output is not allowed, so we add
    # dummy OP_RETURN output, and we need dummy pubkey for it
    dummy_key = CKey.from_secret_bytes(os.urandom(32))

    tx = CMutableTransaction(
        vin=[CTxIn(prevout=COutPoint(hash=lx(txid), n=vout_n))],
        vout=[
            CTxOut(nValue=CConfidentialValue(out_amount),
                   nAsset=CConfidentialAsset(bitcoin_asset),
                   scriptPubKey=dst_addr.to_scriptPubKey(),
                   nNonce=CConfidentialNonce(dst_addr.blinding_pubkey)),
            CTxOut(nValue=CConfidentialValue(0),
                   nAsset=CConfidentialAsset(bitcoin_asset),
                   nNonce=CConfidentialNonce(dummy_key.pub),
                   scriptPubKey=CElementsScript([OP_RETURN])),
            CTxOut(nValue=CConfidentialValue(fee_satoshi),
                   nAsset=CConfidentialAsset(bitcoin_asset))
        ])

    output_pubkeys = [dst_addr.blinding_pubkey, dummy_key.pub]

    in_amount = coins_to_satoshi(pre_agreed_amount)

    input_descriptors = [
        BlindingInputDescriptor(asset=bitcoin_asset,
                                amount=in_amount,
                                blinding_factor=blinding_factor,
                                asset_blinding_factor=asset_blinding_factor)
    ]

    blind_result = tx.blind(input_descriptors=input_descriptors,
                            output_pubkeys=output_pubkeys)

    # The blinding must succeed!
    if blind_result.error:
        die('blind failed: {}'.format(blind_result.error))

    if branch_condition is False:
        # Must set nSequence before we calculate signature hash,
        # because it is included in it
        tx.vin[0].nSequence = elements_contract_timeout

    # We used P2SHCoinAddress to create the address that
    # we sent Elements-BTC to, so we know that we need
    # to use SIGVERSION_BASE

    sighash = elt_contract.sighash(tx,
                                   0,
                                   SIGHASH_ALL,
                                   amount=CConfidentialValue(in_amount),
                                   sigversion=SIGVERSION_BASE)

    spend_sig = spend_key.sign(sighash) + bytes([SIGHASH_ALL])

    if branch_condition is True:
        prepare_elt_spend_reveal_branch(tx, elt_contract, spend_sig,
                                        contract_key, blinding_key)
    else:
        tx.vin[0].scriptSig = CElementsScript(
            [spend_sig, OP_FALSE, elt_contract])

    return tx
예제 #7
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 :-)')
예제 #8
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!')