Beispiel #1
0
    def test_mempool_accept_unconfirmed_ancestors_recovery(self):
        node = self.nodes[0]

        unspents = node.listunspent(minconf=1,
                                    addresses=[str(depositor_address)],
                                    include_unsafe=True,
                                    query_options={"minimumAmount": 0.0})
        new_deposit_transaction(TEST_DATADIR + "/test_deposit_transaction.pkl",
                                unspents)
        deposit_tx = load(TEST_DATADIR + "/test_deposit_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(deposit_tx.serialize()))
        node.sendrawtransaction(b2x(deposit_tx.serialize()))
        self.log.info(
            f"Deposit Transaction with txid {decoded_tx['txid']} accepted.")

        new_vault_transaction(TEST_DATADIR + "/test_vault_transaction.pkl")
        vault_tx = load(TEST_DATADIR + "/test_vault_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(vault_tx.serialize()))
        node.sendrawtransaction(b2x(vault_tx.serialize()))
        self.log.info(
            f"Vault Transaction with txid {decoded_tx['txid']} accepted.")

        new_p2rw_transaction(TEST_DATADIR + "/test_p2rw_transaction.pkl")
        p2rw_tx = load(TEST_DATADIR + "/test_p2rw_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(p2rw_tx.serialize()))
        node.sendrawtransaction(b2x(p2rw_tx.serialize()))
        self.log.info(
            f"P2RW Transaction with txid {decoded_tx['txid']} accepted.")

        new_recover_transaction(TEST_DATADIR + "/test_recover_transaction.pkl")
        recover_tx = load(TEST_DATADIR + "/test_recover_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(recover_tx.serialize()))
        node.sendrawtransaction(b2x(recover_tx.serialize()))
        self.log.info(
            f"Recover Transaction with txid {decoded_tx['txid']} accepted.")
Beispiel #2
0
    def test_vault_theft_recover(self):
        node0 = self.nodes[0]  # Wallet Owner
        node2 = self.nodes[2]  # Attacker

        deposit_address_unspents = node0.listunspent(
            minconf=1,
            addresses=[str(depositor_address)],
            include_unsafe=True,
            query_options={"minimumAmount": 0.0})
        new_deposit_transaction(TEST_DATADIR + "/test_deposit_transaction.pkl",
                                deposit_address_unspents)
        deposit_tx = load(TEST_DATADIR + "/test_deposit_transaction.pkl")
        decoded_tx = node0.decoderawtransaction(b2x(deposit_tx.serialize()))
        node0.sendrawtransaction(b2x(deposit_tx.serialize()))
        self.log.info(
            f"Deposit Transaction with txid {decoded_tx['txid']} accepted.")

        new_vault_transaction(TEST_DATADIR + "/test_vault_transaction.pkl")
        vault_tx = load(TEST_DATADIR + "/test_vault_transaction.pkl")
        decoded_tx = node0.decoderawtransaction(b2x(vault_tx.serialize()))
        node0.sendrawtransaction(b2x(vault_tx.serialize()))
        self.log.info(
            f"Vault Transaction with txid {decoded_tx['txid']} accepted.")

        new_p2rw_transaction(TEST_DATADIR + "/test_p2rw_transaction.pkl")
        p2rw_tx = load(TEST_DATADIR + "/test_p2rw_transaction.pkl")
        fee_wallet_unspents = node0.listunspent(
            minconf=1,
            addresses=[str(fee_wallet_address)],
            include_unsafe=True,
            query_options={"minimumAmount": 0.0})
        p2rw_tx = update_fee(TEST_DATADIR + "/test_p2rw_transaction.pkl",
                             0.0001, fee_wallet_unspents)
        self.log.info("P2RW transaction prepared with high fee.")

        node0.generate(1)
        self.log.info(f"Deposit and Vault Transactions mined.")

        new_unvault_transaction(TEST_DATADIR + "/test_unvault_transaction.pkl")
        unvault_tx = load(TEST_DATADIR + "/test_unvault_transaction.pkl")
        decoded_tx = node2.decoderawtransaction(b2x(unvault_tx.serialize()))
        try:
            node2.sendrawtransaction(b2x(unvault_tx.serialize()))
        except (JSONRPCException):
            self.log.info(
                f"Un-vault Transaction broadcast early by attacker, rejected since time-lock not expired."
            )
        node2.generate(TIMELOCK - 1)
        self.log.info(
            "Time-lock expired, theft transaction broadcast again by attacker."
        )
        node2.sendrawtransaction(b2x(unvault_tx.serialize()))
        node0.sendrawtransaction(b2x(p2rw_tx.serialize()))
        self.log.info(
            f"P2RW Transaction broadcast by wallet owner and replaced attacker's theft transaction."
        )
        node0.generate(1)
        self.log.info(
            f"P2RW Transaction with txid {decoded_tx['txid']} mined.")
Beispiel #3
0
def find_utxo_for_fee(say, die, rpc):
    """Find suitable utxo to pay the fee.
    Retrieve thekey to spend this utxo and add it to
    the returned dict."""

    # Find utxo to use for fee. In our simple example, only Alice pays the fee.
    # To be on a safe side, include only transactions
    # that are confirmed (1 as 'minconf' argument of listunspent)
    # and safe to spend (False as 'include_unsafe' # argument of listunspent)
    say('Searching for utxo for fee asset')
    utxo_list = rpc.listunspent(1, 9999999, [], False,
                                {'asset': fee_asset.to_hex()})
    utxo_list.sort(key=lambda u: u['amount'])
    for utxo in utxo_list:
        # To not deal with possibility of dust outputs,
        # just require fee utxo to be big enough
        if coins_to_satoshi(utxo['amount']) >= FIXED_FEE_SATOSHI * 2:
            utxo['key'] = CCoinKey(rpc.dumpprivkey(utxo['address']))
            if 'assetcommitment' not in utxo:
                # If UTXO is not blinded, Elements daemon will not
                # give us assetcommitment, so we need to generate it ourselves.
                asset = CAsset(lx(utxo['asset']))
                utxo['assetcommitment'] = b2x(asset.to_commitment())
            return utxo
    else:
        die('Cannot find utxo for fee that is >= {} satoshi'.format(
            FIXED_FEE_SATOSHI * 2))
Beispiel #4
0
    def check_unblind(self, unblinded_tx, unblinded_tx_raw, blinded_tx,
                      blinded_tx_raw, bundle, blinding_derivation_key):
        for n, bvout in enumerate(blinded_tx.vout):
            uvout = unblinded_tx.vout[n]

            if not uvout.nValue.is_explicit():
                # skip confidential vouts of partially-blinded txs
                continue

            self.assertEqual(bvout.scriptPubKey, uvout.scriptPubKey)
            if bvout.nAsset.is_explicit():
                self.assertTrue(bvout.nValue.is_explicit())
                self.assertEqual(bvout.nValue.to_amount(),
                                 uvout.nValue.to_amount())
                self.assertEqual(bvout.nAsset.to_asset().data,
                                 uvout.nAsset.to_asset().data)
                self.assertEqual(bvout.nNonce.commitment,
                                 uvout.nNonce.commitment)
            else:
                self.assertFalse(bvout.nValue.is_explicit())

                for fbk, spk_set in bundle['foreign_blinding_keys'].items():
                    if b2x(bvout.scriptPubKey) in spk_set:
                        blinding_key = uvout.scriptPubKey.derive_blinding_key(
                            CKey(lx(fbk)))
                        break
                else:
                    blinding_key = uvout.scriptPubKey.derive_blinding_key(
                        blinding_derivation_key)

                unblind_result = bvout.unblind_confidential_pair(
                    blinding_key=blinding_key,
                    rangeproof=blinded_tx.wit.vtxoutwit[n].rangeproof)

                self.assertFalse(unblind_result.error)
                self.assertEqual(uvout.nValue.to_amount(),
                                 unblind_result.amount)
                self.assertEqual(uvout.nAsset.to_asset().data,
                                 unblind_result.asset.data)
                descr = unblind_result.get_descriptor()

                self.assertIsInstance(descr, BlindingInputDescriptor)
                self.assertEqual(descr.amount, unblind_result.amount)
                self.assertEqual(descr.asset, unblind_result.asset)
                self.assertEqual(descr.blinding_factor,
                                 unblind_result.blinding_factor)
                self.assertEqual(descr.asset_blinding_factor,
                                 unblind_result.asset_blinding_factor)

                ub_info = bundle['unblinded_vout_info'][n]
                if len(ub_info):
                    self.assertEqual(coins_to_satoshi(ub_info['amount']),
                                     unblind_result.amount)
                    self.assertEqual(ub_info['asset'],
                                     unblind_result.asset.to_hex())
                    self.assertEqual(ub_info['blinding_factor'],
                                     unblind_result.blinding_factor.to_hex())
                    self.assertEqual(
                        ub_info['asset_blinding_factor'],
                        unblind_result.asset_blinding_factor.to_hex())
Beispiel #5
0
def try_reclaim_btc(say, btc_rpc, txid, btc_contract, key, die):

    ensure_rpc_connected(say, btc_rpc)

    # we won't return from this function, so we can just
    # set the chain with select_chain_params
    select_chain_params(bitcoin_chain_name)

    def custom_die(msg):
        say(msg)
        die('Failed to reclaim my Bitcoin')

    from_addr = P2WSHCoinAddress.from_redeemScript(btc_contract)

    say('Will try to reclaim my bitcoin from {}'.format(from_addr))

    tx_json = btc_rpc.getrawtransaction(txid, 1)
    confirmations = int(tx_json['confirmations'])

    while confirmations < bitcoin_contract_timeout:
        tx_json = btc_rpc.getrawtransaction(txid, 1)
        confirmations = int(tx_json['confirmations'])

    for vout in tx_json['vout']:
        if 'scriptPubKey' in vout:
            if str(from_addr) in vout['scriptPubKey']['addresses']:
                vout_n = int(vout['n'])
                say('({} at UTXO {}:{})'.format(vout['value'], txid, vout_n))
                break
    else:
        custom_die(
            'Cannot find {} in outputs of tx {} - this must be a bug.'.format(
                from_addr, txid))

    # We should not use CBitcoinAddress directly here, because we might be
    # in regtest or testnet, and it is treated as different chain.
    # CBitcoinAddress will not recognize regtest address, you would need
    # to use CBitcoinTestnetAddress/CBitcoinRegtestAddress.
    # CCoinAddress is the correct abstraction to use.
    dst_addr = CCoinAddress(btc_rpc.getnewaddress())

    say('Will reclaim my Bitcoin to {}'.format(dst_addr))
    reclaim_tx = create_btc_spend_tx(dst_addr,
                                     txid,
                                     vout_n,
                                     btc_contract,
                                     spend_key=key,
                                     branch_condition=False)

    say('Sending my Bitcoin-reclaim transaction')
    new_txid = btc_rpc.sendrawtransaction(b2x(reclaim_tx.serialize()))

    wait_confirm(say, 'Bitcoin', new_txid, custom_die, btc_rpc, num_confirms=3)

    say('Reclaimed my Bitcoin. Swap failed.')
Beispiel #6
0
def try_reclaim_elt(say, elt_rpc, txid, elt_contract, key, blinding_key, die):

    ensure_rpc_connected(say, elt_rpc)

    # we won't return from this function, so we can just
    # set the chain with select_chain_params
    select_chain_params(elements_chain_name)

    from_addr = P2SHCoinAddress.from_redeemScript(elt_contract)

    say('Will try to reclaim my Elements bitcoin asset from {}'.format(
        from_addr))

    tx_json = elt_rpc.getrawtransaction(txid, 1)
    confirmations = int(tx_json['confirmations'])

    while confirmations < elements_contract_timeout:
        tx_json = elt_rpc.getrawtransaction(txid, 1)
        confirmations = int(tx_json['confirmations'])

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

    vout_n, unblind_result = find_and_unblind_vout(say, commit_tx, from_addr,
                                                   blinding_key, die)

    dst_addr = CElementsAddress(elt_rpc.getnewaddress())

    say('Will reclaim my Elements asset to {}'.format(dst_addr))
    reclaim_tx = create_elt_spend_tx(
        dst_addr,
        txid,
        vout_n,
        elt_contract,
        die,
        spend_key=key,
        blinding_factor=unblind_result.blinding_factor,
        asset_blinding_factor=unblind_result.asset_blinding_factor,
        branch_condition=False)

    say('Sending my Elements-reclaim transaction')
    new_txid = elt_rpc.sendrawtransaction(b2x(reclaim_tx.serialize()))

    def custom_die(msg):
        say(msg)
        die('Failed to reclaim by Elemets asset')

    wait_confirm(say,
                 'Elements',
                 new_txid,
                 custom_die,
                 elt_rpc,
                 num_confirms=3)

    say('Reclaimed my Elements asset. Swap failed.')
Beispiel #7
0
    def test_mempool_accept_unconfirmed_ancestors_unvault(self):
        node = self.nodes[0]

        unspents = node.listunspent(minconf=1,
                                    addresses=[str(depositor_address)],
                                    include_unsafe=True,
                                    query_options={"minimumAmount": 0.0})
        new_deposit_transaction(TEST_DATADIR + "/test_deposit_transaction.pkl",
                                unspents)
        deposit_tx = load(TEST_DATADIR + "/test_deposit_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(deposit_tx.serialize()))
        node.sendrawtransaction(b2x(deposit_tx.serialize()))
        self.log.info(
            f"Deposit Transaction with txid {decoded_tx['txid']} accepted.")

        new_vault_transaction(TEST_DATADIR + "/test_vault_transaction.pkl")
        vault_tx = load(TEST_DATADIR + "/test_vault_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(vault_tx.serialize()))
        node.sendrawtransaction(b2x(vault_tx.serialize()))
        self.log.info(
            f"Vault Transaction with txid {decoded_tx['txid']} accepted.")

        new_unvault_transaction(TEST_DATADIR + "/test_unvault_transaction.pkl")
        unvault_tx = load(TEST_DATADIR + "/test_unvault_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(unvault_tx.serialize()))
        try:
            node.sendrawtransaction(b2x(unvault_tx.serialize()))
        except (JSONRPCException):
            self.log.info(
                f"Un-vault Transaction with txid {decoded_tx['txid']} rejected (non-BIP68-final)."
            )
    def test(self) -> None:
        # Test Vectors for RFC 6979 ECDSA, secp256k1, SHA-256
        # (private key, message, expected k, expected signature)
        test_vectors = [
            (0x1, "Satoshi Nakamoto", 0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15, "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"),
            (0x1, "All those moments will be lost in time, like tears in rain. Time to die...", 0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3, "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"),
            (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, "Satoshi Nakamoto", 0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90, "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"),
            (0xf8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181, "Alan Turing", 0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1, "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"),
            (0xe91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2, "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!", 0x1F4B84C23A86A221D233F2521BE018D9318639D5B8BBD6374A8A59232D16AD3D, "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6")
        ]
        for vector in test_vectors:
            secret = CBitcoinKey.from_secret_bytes(x('{:064x}'.format(vector[0])))
            encoded_sig = secret.sign(hashlib.sha256(vector[1].encode('utf8')).digest())

            assert(encoded_sig[0] == 0x30)
            assert(encoded_sig[1] == len(encoded_sig)-2)
            assert(encoded_sig[2] == 0x02)

            rlen = encoded_sig[3]
            rpos = 4
            assert(rlen in (32, 33))

            if rlen == 33:
                assert(encoded_sig[rpos] == 0)
                rpos += 1
                rlen -= 1

            rval = encoded_sig[rpos:rpos+rlen]
            spos = rpos+rlen
            assert(encoded_sig[spos] == 0x02)

            spos += 1
            slen = encoded_sig[spos]
            assert(slen in (32, 33))

            spos += 1
            if slen == 33:
                assert(encoded_sig[spos] == 0)
                spos += 1
                slen -= 1

            sval = encoded_sig[spos:spos+slen]
            sig = b2x(rval + sval)
            assert(str(sig) == vector[3])
 def __new__(cls, wallet: WatchOnlyWallet):
     wallet_data = {
         "wallet_xpub":
         str(wallet.xpub),
         "master_fingerprint":
         b2x(wallet.master_fingerprint),
         "base_keypath":
         str(wallet.base_keypath),
         "current_block":
         wallet.current_block.to_json(),
         "external_addresses": [
             address.to_json()
             for address in wallet.external_addresses.values()
         ],
         "change_addresses": [
             address.to_json()
             for address in wallet.change_addresses.values()
         ],
     }
     with open(cls.PATH, "w") as wallet_file:
         json.dump(wallet_data, wallet_file, indent=4)
Beispiel #10
0
    def test_unvault_tx_flow(self):
        node = self.nodes[0]

        unspents = node.listunspent(minconf=1,
                                    addresses=[str(depositor_address)],
                                    include_unsafe=True,
                                    query_options={"minimumAmount": 0.0})
        new_deposit_transaction(TEST_DATADIR + "/test_deposit_transaction.pkl",
                                unspents)
        deposit_tx = load(TEST_DATADIR + "/test_deposit_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(deposit_tx.serialize()))
        node.sendrawtransaction(b2x(deposit_tx.serialize()))
        self.log.info(
            f"Deposit Transaction with txid {decoded_tx['txid']} accepted.")
        node.generate(1)
        self.log.info(
            f"Deposit Transaction with txid {decoded_tx['txid']} mined.")

        new_vault_transaction(TEST_DATADIR + "/test_vault_transaction.pkl")
        vault_tx = load(TEST_DATADIR + "/test_vault_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(vault_tx.serialize()))
        node.sendrawtransaction(b2x(vault_tx.serialize()))
        self.log.info(
            f"Vault Transaction with txid {decoded_tx['txid']} accepted.")
        node.generate(1)
        self.log.info(
            f"Vault Transaction with txid {decoded_tx['txid']} mined.")

        new_unvault_transaction(TEST_DATADIR + "/test_unvault_transaction.pkl")
        unvault_tx = load(TEST_DATADIR + "/test_unvault_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(unvault_tx.serialize()))
        try:  # Expect transaction rejection (non-BIP68-final)
            node.sendrawtransaction(b2x(unvault_tx.serialize()))
        except (JSONRPCException):
            node.generate(TIMELOCK - 1)  # Wait for time-lock to expire
            node.sendrawtransaction(b2x(unvault_tx.serialize()))
        self.log.info(
            f"Un-vault Transaction with txid {decoded_tx['txid']} accepted.")
        node.generate(1)
        self.log.info(
            f"Un-vault Transaction with txid {decoded_tx['txid']} mined.")
    def test_tx_valid(self):
        for prevouts, tx, tx_data, enforceP2SH in load_test_vectors(
                'tx_valid.json'):
            self.assertEqual(tx_data, tx.serialize())
            self.assertEqual(
                tx_data,
                CTransaction.deserialize(tx.serialize()).serialize())
            try:
                CheckTransaction(tx)
            except CheckTransactionError:
                self.fail('tx failed CheckTransaction(): ' +
                          str((prevouts, b2x(tx.serialize()), enforceP2SH)))
                continue

            for i in range(len(tx.vin)):
                flags = set()
                if enforceP2SH:
                    flags.add(SCRIPT_VERIFY_P2SH)

                VerifyScript(tx.vin[i].scriptSig,
                             prevouts[tx.vin[i].prevout],
                             tx,
                             i,
                             flags=flags)
Beispiel #12
0
    for n in path:
        print("child number: 0x{:08x}".format(n))
        xkey = xkey.derive(n)
        if isinstance(xkey, CBitcoinExtKey):
            print("xpriv:", xkey)

            # Note:
            # if xkey is CBitcoinExtKey, xkey.priv is CBitcoinKey
            #     CBitcoinKey is in WIF format, and compressed
            #     len(bytes(xkey.privkey)) == 33
            # if xkey is CExtKey, xkey.priv is CKey
            #     CKey is always 32 bytes
            #
            # Standalone CBitcoinKey key can be uncompressed,
            # and be of 32 bytes length, but this is not the case
            # with xpriv encapsulated in CBitcoinExtKey - it is
            # always compressed there.
            #
            # you can always use xkey.priv.secret_bytes
            # to get raw 32-byte secret data from both CBitcoinKey and CKey
            #
            print("priv WIF:", xkey.priv)
            print("raw priv:", b2x(xkey.priv.secret_bytes))

            print("xpub: ", xkey.neuter())
            print("pub:", b2x(xkey.pub))
        else:
            assert isinstance(xkey, CBitcoinExtPubKey)
            print("xpub:", xkey)
            print("pub:", b2x(xkey.pub))
Beispiel #13
0
    def check_serialize_deserialize(self, tx, tx_bytes, tx_decoded):
        self.assertEqual(tx_bytes, tx.serialize())
        self.assertEqual(tx_bytes,
                         CTransaction.deserialize(tx.serialize()).serialize())
        self.assertEqual(tx_bytes, tx.to_mutable().to_immutable().serialize())
        self.assertEqual(tx_decoded['version'], tx.nVersion)
        self.assertEqual(tx_decoded['locktime'], tx.nLockTime)
        # we ignore withash field - we do not have ComputeWitnessHash() function
        # as it is only relevant for blocks, not transactions
        self.assertEqual(tx_decoded['hash'], b2lx(tx.GetHash()))
        self.assertEqual(tx_decoded['txid'], b2lx(tx.GetTxid()))
        for n, vout in enumerate(tx_decoded['vout']):
            if 'amountcommitment' in vout:
                self.assertEqual(x(vout['amountcommitment']),
                                 tx.vout[n].nValue.commitment)
            if 'assetcommitment' in vout:
                self.assertEqual(x(vout['assetcommitment']),
                                 tx.vout[n].nAsset.commitment)
            if 'asset' in vout:
                self.assertEqual(vout['asset'],
                                 tx.vout[n].nAsset.to_asset().to_hex())
            if 'scriptPubKey' in vout:
                spk = vout['scriptPubKey']
                self.assertEqual(x(spk['hex']), tx.vout[n].scriptPubKey)

                if 'pegout_type' in spk:
                    self.assertEqual(spk['type'], 'nulldata')
                    self.assertTrue(tx.vout[n].scriptPubKey.is_pegout())
                    genesis_hash, pegout_scriptpubkey = tx.vout[
                        n].scriptPubKey.get_pegout_data()
                    if spk['pegout_type'] != 'nonstandard':
                        assert spk['pegout_type'] in ('pubkeyhash',
                                                      'scripthash')
                        addr = CCoinAddress.from_scriptPubKey(
                            pegout_scriptpubkey)
                        self.assertEqual(len(spk['pegout_addresses']), 1)
                        self.assertEqual(spk['pegout_addresses'][0], str(addr))
                    self.assertEqual(spk['pegout_hex'],
                                     b2x(pegout_scriptpubkey))
                    self.assertEqual(spk['pegout_chain'], b2lx(genesis_hash))

                if spk['type'] in ('pubkeyhash', 'scripthash'):
                    self.assertEqual(len(spk['addresses']), 1)
                    addr = CCoinAddress.from_scriptPubKey(
                        tx.vout[n].scriptPubKey)
                    self.assertEqual(spk['addresses'][0], str(addr))
                elif spk['type'] == 'nulldata':
                    self.assertEqual(tx.vout[n].scriptPubKey, x(spk['hex']))
                else:
                    self.assertEqual(spk['type'], 'fee')
                    self.assertEqual(len(tx.vout[n].scriptPubKey), 0)

            if secp256k1_has_zkp:
                if tx.wit.is_null():
                    rpinfo = None
                else:
                    rpinfo = tx.wit.vtxoutwit[n].get_rangeproof_info()
                if 'value-minimum' in vout:
                    self.assertIsNotNone(rpinfo)
                    self.assertEqual(vout['ct-exponent'], rpinfo.exp)
                    self.assertEqual(vout['ct-bits'], rpinfo.mantissa)
                    self.assertEqual(
                        coins_to_satoshi(vout['value-minimum'],
                                         check_range=False), rpinfo.value_min)
                    self.assertEqual(
                        coins_to_satoshi(vout['value-maximum'],
                                         check_range=False), rpinfo.value_max)
                else:
                    self.assertTrue(rpinfo is None or rpinfo.exp == -1)
                    if rpinfo is None:
                        value = tx.vout[n].nValue.to_amount()
                    else:
                        value = rpinfo.value_min
                    self.assertEqual(coins_to_satoshi(vout['value']), value)
            else:
                warn_zkp_unavailable()
                if 'value' in vout and tx.vout[n].nValue.is_explicit():
                    self.assertEqual(coins_to_satoshi(vout['value']),
                                     tx.vout[n].nValue.to_amount())

        for n, vin in enumerate(tx_decoded['vin']):
            if 'scripSig' in vin:
                self.assertEqual(
                    x(vin['scriptSig']['hex'], tx.vin[n].scriptSig))
            if 'txid' in vin:
                self.assertEqual(vin['txid'], b2lx(tx.vin[n].prevout.hash))
            if 'vout' in vin:
                self.assertEqual(vin['vout'], tx.vin[n].prevout.n)
            if 'is_pegin' in vin:
                self.assertEqual(vin['is_pegin'], tx.vin[n].is_pegin)
                if vin['is_pegin'] is False:
                    if 'scriptWitness' in vin:
                        self.assertTrue(
                            tx.wit.vtxinwit[n].scriptWitness.is_null())
                    if 'pegin_witness' in vin:
                        self.assertTrue(
                            tx.wit.vtxinwit[n].pegin_witness.is_null())
                else:
                    for stack_index, stack_item in enumerate(
                            vin['scriptWitness']):
                        self.assertTrue(
                            stack_item,
                            b2x(tx.wit.vtxinwit[n].scriptWitness.
                                stack[stack_index]))
                    for stack_index, stack_item in enumerate(
                            vin['pegin_witness']):
                        self.assertTrue(
                            stack_item,
                            b2x(tx.wit.vtxinwit[n].pegin_witness.
                                stack[stack_index]))
            if 'sequence' in vin:
                self.assertEqual(vin['sequence'], tx.vin[n].nSequence)
            if 'coinbase' in vin:
                self.assertTrue(tx.is_coinbase())
            if 'issuance' in vin:
                iss = vin['issuance']
                self.assertEqual(
                    iss['assetBlindingNonce'],
                    tx.vin[n].assetIssuance.assetBlindingNonce.to_hex())
                if 'asset' in iss:
                    if iss['isreissuance']:
                        self.assertTrue(not tx.vin[n].assetIssuance.
                                        assetBlindingNonce.is_null())
                        self.assertEqual(
                            iss['assetEntropy'],
                            tx.vin[n].assetIssuance.assetEntropy.to_hex())
                        asset = calculate_asset(
                            tx.vin[n].assetIssuance.assetEntropy)
                    else:
                        entropy = generate_asset_entropy(
                            tx.vin[n].prevout,
                            tx.vin[n].assetIssuance.assetEntropy)
                        self.assertEqual(iss['assetEntropy'], entropy.to_hex())
                        asset = calculate_asset(entropy)
                        reiss_token = calculate_reissuance_token(
                            entropy,
                            tx.vin[n].assetIssuance.nAmount.is_commitment())
                        self.assertEqual(iss['token'], reiss_token.to_hex())
                    self.assertEqual(iss['asset'], asset.to_hex())
                if 'assetamount' in iss:
                    self.assertEqual(
                        coins_to_satoshi(iss['assetamount']),
                        tx.vin[n].assetIssuance.nAmount.to_amount())
                elif 'assetamountcommitment' in iss:
                    self.assertEqual(
                        iss['assetamountcommitment'],
                        b2x(tx.vin[n].assetIssuance.nAmount.commitment))
                if 'tokenamount' in iss:
                    self.assertEqual(
                        coins_to_satoshi(iss['tokenamount']),
                        tx.vin[n].assetIssuance.nInflationKeys.to_amount())
                elif 'tokenamountcommitment' in iss:
                    self.assertEqual(
                        iss['tokenamountcommitment'],
                        b2x(tx.vin[n].assetIssuance.nInflationKeys.commitment))
 def T(base58_privkey: str, expected_hex_pubkey: str, expected_is_compressed_value: bool) -> None:
     key = CBitcoinKey(base58_privkey)
     self.assertEqual(b2x(key.pub), expected_hex_pubkey)
     self.assertEqual(key.is_compressed(), expected_is_compressed_value)
        def T(str_addr: str, expected_scriptPubKey_hexbytes: str) -> None:
            addr = CBitcoinAddress(str_addr)

            actual_scriptPubKey = addr.to_scriptPubKey()
            self.assertEqual(b2x(actual_scriptPubKey), expected_scriptPubKey_hexbytes)
Beispiel #16
0
 def T(base58_privkey, expected_hex_pubkey,
       expected_is_compressed_value):
     key = CBitcoinSecret(base58_privkey)
     self.assertEqual(b2x(key.pub), expected_hex_pubkey)
     self.assertEqual(key.is_compressed, expected_is_compressed_value)
)
from bitcointx.core.scripteval import VerifyScript
from bitcointx.wallet import CBitcoinAddress, CBitcoinKey

select_chain_params('bitcoin')

COIN = CoreCoinParams.COIN

# Create the (in)famous correct brainwallet secret key.
h = hashlib.sha256(b'correct horse battery staple').digest()
seckey = CBitcoinKey.from_secret_bytes(h)

# Create a redeemScript. Similar to a scriptPubKey the redeemScript must be
# satisfied for the funds to be spent.
txin_redeemScript = CScript([seckey.pub, OP_CHECKSIG])
print(b2x(txin_redeemScript))

# Create the magic P2SH scriptPubKey format from that redeemScript. You should
# look at the CScript.to_p2sh_scriptPubKey() function in bitcointx.core.script
# to understand what's happening, as well as read BIP16:
# https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()

# Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it.
# You'll need to send some funds to it to create a txout to spend.
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
print('Pay to:', str(txin_p2sh_address))

# Same as the txid:vout the createrawtransaction RPC call requires
#
# lx() takes *little-endian* hex and converts it to bytes; in Bitcoin
Beispiel #18
0
    def test_RBF(self):
        node = self.nodes[0]

        deposit_address_unspents = node.listunspent(
            minconf=1,
            addresses=[str(depositor_address)],
            include_unsafe=True,
            query_options={"minimumAmount": 0.0})
        new_deposit_transaction(TEST_DATADIR + "/test_deposit_transaction.pkl",
                                deposit_address_unspents)
        deposit_tx = load(TEST_DATADIR + "/test_deposit_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(deposit_tx.serialize()))
        node.sendrawtransaction(b2x(deposit_tx.serialize()))
        self.log.info(
            f"Deposit Transaction with txid {decoded_tx['txid']} accepted.")

        new_vault_transaction(TEST_DATADIR + "/test_vault_transaction.pkl")
        vault_tx = load(TEST_DATADIR + "/test_vault_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(vault_tx.serialize()))
        node.sendrawtransaction(b2x(vault_tx.serialize()))
        self.log.info(
            f"Vault Transaction with txid {decoded_tx['txid']} accepted.")
        node.generate(TIMELOCK)
        self.log.info(f"Deposit and Vault Transactions mined.")

        self.sync_all(
        )  # Synce node 0 and 1 after mining deposit and vault transactions.
        disconnect_nodes(
            self.nodes[0], 1
        )  # Now split the network to test transaction types on different nodes.

        # Use node 0 to test update fee for unvault transaction
        new_unvault_transaction(TEST_DATADIR + "/test_unvault_transaction.pkl")
        unvault_tx = load(TEST_DATADIR + "/test_unvault_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(unvault_tx.serialize()))
        node.sendrawtransaction(b2x(unvault_tx.serialize()))
        self.log.info(
            f"Un-vault Transaction with txid {decoded_tx['txid']} accepted by node 0."
        )
        fee_wallet_unspents = node.listunspent(
            minconf=1,
            addresses=[str(fee_wallet_address)],
            include_unsafe=True,
            query_options={"minimumAmount": 0.0})
        unvault_tx = update_fee(TEST_DATADIR + "/test_unvault_transaction.pkl",
                                0.0001, fee_wallet_unspents)
        decoded_tx = node.decoderawtransaction(b2x(unvault_tx.serialize()))
        node.sendrawtransaction(b2x(unvault_tx.serialize()))
        self.log.info(
            f"Updated Un-vault Transaction with txid {decoded_tx['txid']} accepted by node 0."
        )
        node.generate(1)
        self.log.info(
            f"Un-vault Transaction with txid {decoded_tx['txid']} mined by node 0."
        )

        # Use node 1 to test update fee for pr2w transaction
        node = self.nodes[1]
        new_p2rw_transaction(TEST_DATADIR + "/test_p2rw_transaction.pkl")
        p2rw_tx = load(TEST_DATADIR + "/test_p2rw_transaction.pkl")
        decoded_tx = node.decoderawtransaction(b2x(p2rw_tx.serialize()))
        node.sendrawtransaction(b2x(p2rw_tx.serialize()))
        self.log.info(
            f"P2RW Transaction with txid {decoded_tx['txid']} accepted by node 1."
        )
        fee_wallet_unspents = node.listunspent(
            minconf=1,
            addresses=[str(fee_wallet_address)],
            include_unsafe=True,
            query_options={"minimumAmount": 0.0})
        p2rw_tx = update_fee(TEST_DATADIR + "/test_p2rw_transaction.pkl",
                             0.0001, fee_wallet_unspents)
        decoded_tx = node.decoderawtransaction(b2x(p2rw_tx.serialize()))
        node.sendrawtransaction(b2x(p2rw_tx.serialize()))
        self.log.info(
            f"Updated P2RW Transaction with txid {decoded_tx['txid']} accepted by node 1."
        )
        node.generate(1)
        self.log.info(
            f"P2RW Transaction with txid {decoded_tx['txid']} mined by node 1."
        )
 def T(redeemScript_ops: List[Union[int, bytes]],
       expected_hex_bytes: str) -> None:
     redeemScript = CScript(redeemScript_ops)
     actual_script = redeemScript.to_p2sh_scriptPubKey()
     self.assertEqual(b2x(actual_script), expected_hex_bytes)
Beispiel #20
0
    if not sign_result.is_final:
        print(f'{sign_result.num_inputs_ready} inputs is ready '
              f'to be finalized\n')
    else:
        assert sign_result.num_inputs_ready == 0

    if not sign_result.is_final and sign_result.num_inputs_signed > 0:
        for index, info in enumerate(sign_result.inputs_info):
            if info:
                print(f"Input {index}: added {info.num_new_sigs} sigs, ",
                      end='')
                print(f"input is now final" if info.is_final else
                      f"{info.num_sigs_missing} is still missing")
            else:
                print(f"Input {index}: skipped, cannot be processed")

    print()
    if args.finalize:
        if not sign_result.is_final:
            print(f'Failed to finalize transaction')
            sys.exit(-1)

        print("Signed network transaction:\n")
        print(b2x(psbt.extract_transaction().serialize()))
    elif sign_result.num_inputs_signed == 0:
        print("Could not sign any inputs")
        sys.exit(-1)
    else:
        print("PSBT with added signatures:\n")
        print(psbt.to_base64())
Beispiel #21
0
 def T(base58_xpubkey, expected_hex_xpubkey):
     key = CBitcoinExtPubKey(base58_xpubkey)
     self.assertEqual(b2x(key), expected_hex_xpubkey)
     key2 = CBitcoinExtPubKey.from_bytes(x(expected_hex_xpubkey))
     self.assertEqual(b2x(key), b2x(key2))
Beispiel #22
0
def bob(say, recv, send, die, rpc):
    """A function that implements the logic
    of the second participant of an asset atomic swap"""

    # Issue an asset that we are going to swap
    asset_str, asset_utxo = issue_asset(say, 1.0, rpc)
    asset_amount_satoshi = coins_to_satoshi(asset_utxo['amount'])

    say('Setting up communication with Alice')

    # Wait for Alice to start communication
    recv('ready')
    # To avoid mempool synchronization problems in two-node regtest setup,
    # in our example Alice is the one in charge of generating test blocks.
    # Send txid of asset issuance to alice so she can ensure it is confirmed.
    send('wait-txid-confirm', asset_utxo['txid'])

    say('Waiting for Alice to send us an offer array')

    alice_offers = recv('offer')

    # We unconditionally accept Alice's offer - her assets are
    # equally worthless as our asset :-)

    say("Alice's offers are {}, sending my offer".format(alice_offers))

    my_offer = AtomicSwapOffer(amount=asset_amount_satoshi, asset=asset_str)

    send('offer', my_offer)

    say('Waiting for Alice\'s address and assetcommitments')

    alice_addr_str, alice_assetcommitments = recv('addr_and_assetcommitments')

    print_asset_balances(say, alice_offers + [my_offer], rpc)

    # Convert Alice's address to address object.
    # If Alice passes invalid address, we die with we die with exception.
    alice_addr = CCoinAddress(alice_addr_str)

    say('Alice\'s address: {}'.format(alice_addr))
    say('Alice\'s assetcommitments: {}'.format(alice_assetcommitments))

    # Create asset commitments array. First goes our own asset commitment,
    # because our UTXO will be first.
    assetcommitments = [x(asset_utxo['assetcommitment'])]
    for ac in alice_assetcommitments:
        # If Alice sends non-hex data, we will die while converting.
        assetcommitments.append(x(ac))

    # Let's create our part of the transaction. We need to create
    # mutable transaction, because blind() method only works for mutable.
    partial_tx = CMutableTransaction(
        vin=[
            CTxIn(prevout=COutPoint(hash=lx(asset_utxo['txid']),
                                    n=asset_utxo['vout']))
        ],
        vout=[
            CTxOut(nValue=CConfidentialValue(asset_amount_satoshi),
                   nAsset=CConfidentialAsset(CAsset(lx(asset_str))),
                   scriptPubKey=alice_addr.to_scriptPubKey())
        ])

    # Blind our part of transaction, specifying assetcommitments
    # (Incliding those received from Alice) as auxiliary_generators.

    # Note that we could get the blinding factors if we retrieve
    # the transaction that we spend from, deserialize it, and unblind
    # the output that we are going to spend.
    # We could do everything here (besides issuing the asset and sending
    # the transactions) without using Elements RPC, if we get our data
    # from files or database, etc. But to simplify our demonstration,
    # we will use the values we got from RPC.

    # See 'spend-to-confidential-address.py' example for the code
    # that does the unblinding itself, and uses the unblinded values
    # to create a spending transaction.

    blind_result = partial_tx.blind(
        input_descriptors=[
            BlindingInputDescriptor(
                asset=CAsset(lx(asset_utxo['asset'])),
                amount=asset_amount_satoshi,
                blinding_factor=Uint256(lx(asset_utxo['amountblinder'])),
                asset_blinding_factor=Uint256(lx(asset_utxo['assetblinder'])))
        ],
        output_pubkeys=[alice_addr.blinding_pubkey],
        auxiliary_generators=assetcommitments)

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

    # And must blind exactly one output
    if blind_result.num_successfully_blinded != 1:
        die('blinded {} outputs, expected to be 1'.format(
            blind_result.num_successfully_blinded))

    say('Successfully blinded partial transaction, sending it to Alice')

    send('partial_blinded_tx', partial_tx.serialize())

    say("Generating addresses to receive Alice's assets")
    # Generate as many destination addresses as there are assets
    # in Alice's offer. Record blinding keys for the addresses.
    our_addrs = []
    blinding_keys = []
    for _ in alice_offers:
        addr, blinding_key = get_dst_addr(say, rpc)
        our_addrs.append(str(addr))
        blinding_keys.append(blinding_key)

    say("Sending my addresses and assetcommitment to Alice")
    send('addr_list_and_assetcommitment',
         (our_addrs, asset_utxo['assetcommitment']))

    semi_signed_tx_bytes = recv('partially_signed_tx')

    say('Got partially signed tx of size {} bytes from Alice'.format(
        len(semi_signed_tx_bytes)))

    semi_signed_tx = CTransaction.deserialize(semi_signed_tx_bytes)

    # Transaction should have 3 extra outputs - one output to Alice,
    # fee output, and fee asset change output
    if len(semi_signed_tx.vout) != len(alice_offers) + 3:
        die('unexpected number of outputs in tx from Alice: '
            'expected {}, got {}'.format(
                len(alice_offers) + 3, len(semi_signed_tx.vout)))

    if not semi_signed_tx.vout[-1].is_fee():
        die('Last output in tx from Alice '
            'is expected to be fee output, but it is not')

    # Unblind outputs that should be directed to us and check
    # that they match the offer. We use n+1 as output index
    # because we skip our own output, which is at index 0.
    for n, offer in enumerate(alice_offers):
        result = semi_signed_tx.vout[n + 1].unblind_confidential_pair(
            blinding_keys[n], semi_signed_tx.wit.vtxoutwit[n + 1].rangeproof)

        if result.error:
            die('cannot unblind output {} that should have been '
                'directed to us: {}'.format(n + 1, result.error))

        if result.asset.to_hex() != offer.asset:
            die("asset at position {} (vout {}) in partial transaction "
                "from Alice {} is not the same as asset in Alice's "
                "initial offer ({})".format(n, n + 1, result.asset.to_hex(),
                                            offer.asset))

        if result.amount != offer.amount:
            die("amount at position {} (vout {}) in partial transaction "
                "from Alice {} is not the same as amount in Alice's "
                "initial offer ({})".format(n, n + 1, result.amount,
                                            offer.amount))

    say("Assets and amounts in partially signed transaction "
        "match Alice's offer")

    # Signing will change the tx, so i
    tx = semi_signed_tx.to_mutable()

    # Our input is at index 0
    sign_input(tx, 0, asset_utxo)

    # Note that at this point both participants can still opt out of the swap:
    # Bob by not broadcasting the transaction, and Alice by double-spending
    # her inputs to the transaction. Bob still have tiny advantage, because
    # he can pretend to have 'difficulties' in broadcasting and try to exploit
    # Alice's patience

    say('Signed the transaction from my side, ready to send')

    tx_hex = b2x(tx.serialize())

    if bob_be_sneaky:
        say('Hey! I am now in control of the final transaction. '
            'I have the option to exectue the swap or abort. ')
        say('Why not wait a bit and watch asset prices, and execute '
            'the swap only if it is profitable')
        say('I will reduce my risk a bit by doing that.')
        # Bob takes his time and is not sending the final
        # transaction to Alice for some time...
        time.sleep(ALICE_PATIENCE_LIMIT + 2)
        say('OK, I am willing to execute the swap now')

    # Send the final transaction to Alice, so she can be sure that
    # we is not cheating
    send('final-signed-tx', tx_hex)

    txid = rpc.sendrawtransaction(tx_hex)

    say('Sent with txid {}'.format(txid))

    # Wait for alice to politely end the conversation
    recv('thanks-goodbye')
    print_asset_balances(say, alice_offers + [my_offer], rpc)

    for i, offer in enumerate(alice_offers):
        balance = coins_to_satoshi(rpc.getbalance("*", 1, False, offer.asset))
        if balance != offer.amount:
            die('something went wrong, asset{} balance after swap should be '
                '{} satoshi, but it is {} satoshi'.format(
                    i, balance, offer.amount))

    say('Asset atomic swap completed successfully')
Beispiel #23
0
 def T(redeemScript, expected_hex_bytes):
     redeemScript = CScript(redeemScript)
     actual_script = redeemScript.to_p2sh_scriptPubKey()
     self.assertEqual(b2x(actual_script), expected_hex_bytes)
 def T(base58_xprivkey, expected_hex_xprivkey):
     key = CBitcoinExtKey(base58_xprivkey)
     self.assertEqual(b2x(key), expected_hex_xprivkey)
        change_addr = rpc.getnewaddress()
        change_pubkey_hex = rpc.getaddressinfo(change_addr)['pubkey']
        change_out = CMutableTxOut(
            CoreCoinParams.MAX_MONEY,
            CScript([x(change_pubkey_hex), OP_CHECKSIG]))

        digest_outs = [CMutableTxOut(0, CScript([OP_RETURN, digest]))]

        txouts = [change_out] + digest_outs

        tx = CTransaction(txins, txouts).to_mutable()

        FEE_PER_VBYTE = 0.00025 * CoreCoinParams.COIN / 1000
        while True:
            required_fee = tx.get_virtual_size() * FEE_PER_VBYTE
            tx.vout[0].nValue = int(value_in -
                                    max(required_fee, 0.00011 *
                                        CoreCoinParams.COIN))

            r = rpc.signrawtransactionwithwallet(b2x(tx.serialize()))
            assert r['complete']
            tx = CTransaction.deserialize(x(r['hex']))

            if value_in - tx.vout[0].nValue >= required_fee:
                tx_bytes = tx.serialize()
                tx_hex = b2x(tx_bytes)
                print(tx_hex)
                print(len(tx_bytes), 'bytes', file=sys.stderr)
                print(rpc.sendrawtransaction(tx_hex))
                break
Beispiel #26
0
        def T(str_addr, expected_scriptPubKey_hexbytes):
            addr = CBitcoinAddress(str_addr)

            actual_scriptPubKey = addr.to_redeemScript()
            self.assertEqual(b2x(actual_scriptPubKey),
                             expected_scriptPubKey_hexbytes)
Beispiel #27
0
def alice(say, recv, send, die, rpc):
    """A function that implements the logic
    of the first participant of an asset atomic swap"""

    # Issue two asset that we are going to swap to Bob's 1 asset
    asset1_str, asset1_utxo = issue_asset(say, 1.0, rpc)
    asset2_str, asset2_utxo = issue_asset(say, 1.0, rpc)

    # We will need to pay a fee in an asset suitable for this
    fee_utxo = find_utxo_for_fee(say, die, rpc)

    say('Getting change address for fee asset')
    # We don't care for blinding key of change - the node will
    # have it, anyway, and we don't need to unblind the change.
    fee_change_addr, _ = get_dst_addr(say, rpc)

    say('Will use utxo {}:{} (amount: {}) for fee, change will go to {}'.
        format(fee_utxo['txid'], fee_utxo['vout'], fee_utxo['amount'],
               fee_change_addr))

    say('Setting up communication with Bob')

    # Tell Bob that we are ready to communicate
    send('ready')

    # To avoid mempool synchronization problems,
    # in our example Alice is the one in charge of generating test blocks.
    # Bob gives alice txid of his transaction that he wants to be confirmed.
    bob_txid = recv('wait-txid-confirm')

    # Make sure asset issuance transactions are confirmed
    rpc.generatetoaddress(1, rpc.getnewaddress())
    wait_confirm(say, asset1_utxo['txid'], die, rpc)
    wait_confirm(say, asset2_utxo['txid'], die, rpc)
    wait_confirm(say, bob_txid, die, rpc)

    # Make sure Bob is alive and ready to communicate, and send
    # him an offer for two assets
    say('Sending offer to Bob')
    my_offers = [
        AtomicSwapOffer(asset=asset1_str,
                        amount=coins_to_satoshi(asset1_utxo['amount'])),
        AtomicSwapOffer(asset=asset2_str,
                        amount=coins_to_satoshi(asset2_utxo['amount']))
    ]
    send('offer', my_offers)

    bob_offer = recv('offer')

    print_asset_balances(say, my_offers + [bob_offer], rpc)

    say('Bob responded with his offer: {}'.format(bob_offer))

    # We unconditionally accept Bob's offer - his asset is
    # equally worthless as ours :-)

    # Generate an address for Bob to send his asset to.
    dst_addr, blinding_key = get_dst_addr(say, rpc)

    say('Sending my address and assetcommitments for my UTXOs to Bob')
    # Send Bob our address, and the assetcommitments of our UTXOs
    # (but not any other information about our UTXO),
    # so he can construct and blind a partial transaction that
    # will spend his own UTXO, to send his asset to our address.
    assetcommitments = [
        asset1_utxo['assetcommitment'], asset2_utxo['assetcommitment'],
        fee_utxo['assetcommitment']
    ]

    send('addr_and_assetcommitments', (str(dst_addr), assetcommitments))

    partial_tx_bytes = recv('partial_blinded_tx')

    say('Got partial blinded tx of size {} bytes from Bob'.format(
        len(partial_tx_bytes)))

    partial_tx = CTransaction.deserialize(partial_tx_bytes)

    if len(partial_tx.vout) != 1:
        die('unexpected number of outputs in tx from Bob: expected 1, got {}'.
            format(len(partial_tx.vout)))

    result = partial_tx.vout[0].unblind_confidential_pair(
        blinding_key, partial_tx.wit.vtxoutwit[0].rangeproof)

    if result.error:
        die('cannot unblind output that should have been directed to us: {}'.
            format(result.error))

    if result.asset.to_hex() != bob_offer.asset:
        die("asset in partial transaction from Bob {} is not the same "
            "as asset in Bob's initial offer ({})".format(
                result.asset.to_hex(), bob_offer.asset))

    if result.amount != bob_offer.amount:
        die("amount in partial transaction from Bob {} is not the same "
            "as amount in Bob's initial offer ({})".format(
                result.amount, bob_offer.amount))

    say("Asset and amount in partial transaction matches Bob's offer")

    bob_addr_list, bob_assetcommitment = recv('addr_list_and_assetcommitment')

    if len(bob_addr_list) != len(my_offers):
        die('unexpected address list lenth from Bob. expected {}, got {}'.
            format(len(my_offers), len(bob_addr_list)))

    say("Bob's addresses to receive my assets: {}".format(bob_addr_list))

    # Convert Bob's addresses to address objects.
    # If Bob passes invalid address, we die with with exception.
    bob_addr_list = [CCoinAddress(a) for a in bob_addr_list]

    # Add our own inputs and outputs to Bob's partial tx

    # Create new mutable transaction from partial_tx
    tx = partial_tx.to_mutable()

    # We have assetcommitment for the first input,
    # other data is not needed for it.
    # initialize first elements of the arrays with empty/negative data.
    input_descriptors = [
        BlindingInputDescriptor(asset=CAsset(),
                                amount=-1,
                                blinding_factor=Uint256(),
                                asset_blinding_factor=Uint256())
    ]

    # First output is already blinded, fill the slot with empty data
    output_pubkeys = [CPubKey()]

    # But assetcommitments array should start with Bob's asset commitment
    assetcommitments = [x(bob_assetcommitment)]

    # We will add our inputs for asset1 and asset2, and also an input
    # that will be used to pay the fee.

    # Note that the order is important: Bob blinded his transaction
    # with assetcommitments in the order we send them to him,
    # and we should add our inputs in the same order.
    utxos_to_add = (asset1_utxo, asset2_utxo, fee_utxo)

    # Add inputs for asset1 and asset2 and fee_asset and prepare input data
    # for blinding
    for utxo in utxos_to_add:
        # When we create CMutableTransaction and pass CTxIn,
        # it will be converted to CMutableTxIn. But if we append
        # to tx.vin or tx.vout, we need to use mutable versions
        # of the txin/txout classes, or else blinding or signing
        # will fail with error, unable to modify the instances.
        # COutPoint is not modified, though, so we can leave it
        # immutable.
        tx.vin.append(
            CMutableTxIn(
                prevout=COutPoint(hash=lx(utxo['txid']), n=utxo['vout'])))
        input_descriptors.append(
            BlindingInputDescriptor(
                asset=CAsset(lx(utxo['asset'])),
                amount=coins_to_satoshi(utxo['amount']),
                blinding_factor=Uint256(lx(utxo['amountblinder'])),
                asset_blinding_factor=Uint256(lx(utxo['assetblinder']))))

        # If we are supplying asset blinders and assetblinders for
        # particular input, assetcommitment data for that input do
        # not need to be correct. But if we are supplying assetcommitments
        # at all (auxiliary_generators argument to tx.blind()),
        # then all the elements of that array must have correct
        # type (bytes) and length (33). This is a requirement of the original
        # Elements Core API, and python-elementstx requires this, too.
        assetcommitments.append(b'\x00' * 33)

    # Add outputs to give Bob all our assets, and fill output pubkeys
    # for blinding the outputs to Bob's addresses
    for n, offer in enumerate(my_offers):
        tx.vout.append(
            CMutableTxOut(nValue=CConfidentialValue(offer.amount),
                          nAsset=CConfidentialAsset(CAsset(lx(offer.asset))),
                          scriptPubKey=bob_addr_list[n].to_scriptPubKey()))
        output_pubkeys.append(bob_addr_list[n].blinding_pubkey)

    # Add change output for fee asset
    fee_change_amount = (coins_to_satoshi(fee_utxo['amount']) -
                         FIXED_FEE_SATOSHI)
    tx.vout.append(
        CMutableTxOut(nValue=CConfidentialValue(fee_change_amount),
                      nAsset=CConfidentialAsset(fee_asset),
                      scriptPubKey=fee_change_addr.to_scriptPubKey()))
    output_pubkeys.append(fee_change_addr.blinding_pubkey)

    # Add fee output.
    # Note that while we use CConfidentialAsset and CConfidentialValue
    # to specify value and asset, they are not in fact confidential here
    # - they are explicit, because we pass explicit values at creation.
    # You can check if they are explicit or confidential
    # with nValue.is_explicit(). If they are explicit, you can access
    # the unblinded values with nValue.to_amount() and nAsset.to_asset()
    tx.vout.append(
        CMutableTxOut(nValue=CConfidentialValue(FIXED_FEE_SATOSHI),
                      nAsset=CConfidentialAsset(fee_asset)))
    # Add dummy pubkey for non-blinded fee output
    output_pubkeys.append(CPubKey())

    # Our transaction lacks txin witness instances for the added inputs,
    # and txout witness instances for added outputs.
    # If transaction already have witness data attached, transaction
    # serialization code will require in/out witness array length
    # to be equal to vin/vout array length
    # Therefore we need to add dummy txin and txout witnesses for each
    # input and output that we added to transaction
    # we added one input and one output per asset, and an additional
    # input/change-output for fee asset.
    for _ in utxos_to_add:
        tx.wit.vtxinwit.append(CMutableTxInWitness())
        tx.wit.vtxoutwit.append(CMutableTxOutWitness())

    # And one extra dummy txout witness for fee output
    tx.wit.vtxoutwit.append(CMutableTxOutWitness())

    # And blind the combined transaction
    blind_result = tx.blind(input_descriptors=input_descriptors,
                            output_pubkeys=output_pubkeys,
                            auxiliary_generators=assetcommitments)

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

    # And must blind exactly three outputs (two to Bob, one fee asset change)
    if blind_result.num_successfully_blinded != 3:
        die('blinded {} outputs, expected to be 3'.format(
            blind_result.num_successfully_blinded))

    say('Successfully blinded the combined transaction, will now sign')

    # Sign two new asset inputs, and fee asset input
    for n, utxo in enumerate(utxos_to_add):
        # We specify input_index as 1+n because we skip first (Bob's) input
        sign_input(tx, 1 + n, utxo)

    say('Signed my inputs, sending partially-signed transaction to Bob')

    send('partially_signed_tx', tx.serialize())

    # Note that at this point both participants can still opt out of the swap:
    # Alice by double-spending her inputs to the transaction,
    # and Bob by not signing or not broadcasting the transaction.
    # Bob still have tiny advantage, because
    # he can pretend to have 'difficulties' in broadcasting and try to exploit
    # Alice's patience. If Alice does not reclaim her funds in the case Bob's
    # behaviour deviates from expected, then Bob will have free option to
    # exectute the swap at the time convenient to him.

    # Get the swap transaction from Bob.
    # Bob is expected to broadcast this transaction, and could just send txid
    # here, but then there would be a period of uncertainty: if Alice do not
    # see the txid at her own node, she does not know if this is because Bob
    # did not actually broadcast, and is just taking his time watching asset
    # prices, or the transaction just takes long time to propagate. If the
    # protocol requires Bob to send the transaction, the timeout required for
    # Alice to wait can be defined much more certainly.
    try:
        signed_tx_raw = recv('final-signed-tx', timeout=ALICE_PATIENCE_LIMIT)
        signed_tx = CTransaction.deserialize(x(signed_tx_raw))
        # Check that this transaction spends the same inputs as the transacton
        # previously agreed upon
        for n, vin in enumerate(signed_tx.vin):
            if vin.prevout != tx.vin[n].prevout:
                die('Inputs of transaction received from Bob do not match '
                    'the agreed-upon transaction')
        # Send the transaction from our side
        txid = rpc.sendrawtransaction(b2x(signed_tx.serialize()))
    except Exception as e:
        # If there is any problem, including communication timeout or invalid
        # communication, or invalid transaction encoding, then Alice will try
        # to claim her funds back, so Bob won't have an option to execute the
        # swap at the time convenient to him. He should execute it immediately.
        say('Unexpected problem on receiving final signed transaction '
            'from Bob: {}'.format(e))
        say('This is suspicious. I will try to reclaim my funds now')
        claim_funds_back(say, utxos_to_add, die, rpc)
        say("Claimed my funds back. Screw Bob!")
        sys.exit(0)

    # Make sure the final transaction is confirmed
    rpc.generatetoaddress(1, rpc.getnewaddress())
    wait_confirm(say, txid, die, rpc)

    # Check that everything went smoothly
    balance = coins_to_satoshi(rpc.getbalance("*", 1, False, bob_offer.asset))
    if balance != bob_offer.amount:
        die('something went wrong, balance of Bob\'s asset after swap '
            'should be {} satoshi, but it is {} satoshi'.format(
                balance, bob_offer.amount))

    print_asset_balances(say, my_offers + [bob_offer], rpc)

    # Wait for alice to politely end the conversation
    send('thanks-goodbye')

    say('Asset atomic swap completed successfully')
Beispiel #28
0
 def T(base58_xprivkey: str, expected_hex_xprivkey: str) -> None:
     key = CBitcoinExtKey(base58_xprivkey)
     self.assertEqual(b2x(key), expected_hex_xprivkey)
     key2 = CBitcoinExtKey.from_bytes(x(expected_hex_xprivkey))
     self.assertEqual(b2x(key), b2x(key2))
Beispiel #29
0
txin_scriptPubKey = \
    P2PKHBitcoinAddress.from_pubkey(seckey.pub).to_scriptPubKey()

# Create the txout. This time we create the scriptPubKey from a Bitcoin
# address.
txout = CMutableTxOut(
    0.001 * COIN,
    CBitcoinAddress('1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8').to_scriptPubKey())

# Create the unsigned transaction.
tx = CMutableTransaction([txin], [txout])

# Calculate the signature hash for that transaction.
sighash = SignatureHash(txin_scriptPubKey, tx, 0, SIGHASH_ALL)

# Now sign it. We have to append the type of signature we want to the end, in
# this case the usual SIGHASH_ALL.
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])

# Set the scriptSig of our transaction input appropriately.
txin.scriptSig = CScript([sig, seckey.pub])

# Verify the signature worked. This calls EvalScript() and actually executes
# the opcodes in the scripts to see if everything worked out. If it doesn't an
# exception will be raised.
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0)

# Done! Print the transaction to standard output with the bytes-to-hex
# function.
print(b2x(tx.serialize()))
Beispiel #30
0
def claim_funds_back(say, utxos, die, rpc):
    """Try to claim our funds by sending our UTXO to our own addresses"""

    # The transaction-building code here does not introduce anything new
    # compared to the code in participant functions, so it will not be
    # commented too much.

    input_descriptors = []
    # It is better to prepare the claw-back transaction beforehand, to avoid
    # the possibility of unexpected problems arising at the critical time when
    # we need to send claw-back tx ASAP, but that would clutter the earlier
    # part of the example with details that are not very relevant there.
    tx = CMutableTransaction()
    for utxo in utxos:
        tx.vin.append(
            CTxIn(prevout=COutPoint(hash=lx(utxo['txid']), n=utxo['vout'])))
        input_descriptors.append(
            BlindingInputDescriptor(
                asset=CAsset(lx(utxo['asset'])),
                amount=coins_to_satoshi(utxo['amount']),
                blinding_factor=Uint256(lx(utxo['amountblinder'])),
                asset_blinding_factor=Uint256(lx(utxo['assetblinder']))))

    asset_amounts = {}
    # If some assets are the same, we want them to be sent to one address
    for idesc in input_descriptors:
        if idesc.asset == fee_asset:
            amount = idesc.amount - FIXED_FEE_SATOSHI
            assert amount >= FIXED_FEE_SATOSHI  # enforced at find_utxo_for_fee
        else:
            amount = idesc.amount

        asset_amounts[idesc.asset] = amount

    output_pubkeys = []
    for asset, amount in asset_amounts.items():
        dst_addr, _ = get_dst_addr(None, rpc)
        tx.vout.append(
            CTxOut(nValue=CConfidentialValue(amount),
                   nAsset=CConfidentialAsset(asset),
                   scriptPubKey=dst_addr.to_scriptPubKey()))
        output_pubkeys.append(dst_addr.blinding_pubkey)

    # Add the explicit fee output
    tx.vout.append(
        CTxOut(nValue=CConfidentialValue(FIXED_FEE_SATOSHI),
               nAsset=CConfidentialAsset(fee_asset)))
    # Add dummy pubkey for non-blinded fee output
    output_pubkeys.append(CPubKey())

    # We used immutable objects for transaction components like CTxIn,
    # just for our convenience. Convert them all to mutable.
    tx = tx.to_immutable().to_mutable()

    # And blind the combined transaction
    blind_result = tx.blind(input_descriptors=input_descriptors,
                            output_pubkeys=output_pubkeys)

    assert (not blind_result.error
            and blind_result.num_successfully_blinded == len(utxos))

    for n, utxo in enumerate(utxos):
        sign_input(tx, n, utxo)

    # It is possible that Bob has actually sent the swap transaction.
    # We will get an error if our node has received this transaction.
    # In real application, we might handle this case, too, but
    # here we will just ignore it.
    txid = rpc.sendrawtransaction(b2x(tx.serialize()))

    rpc.generatetoaddress(1, rpc.getnewaddress())
    wait_confirm(say, txid, die, rpc)