def test_split_blinding_multi_sign(self) -> None:
        if not secp256k1_has_zkp:
            warn_zkp_unavailable()
            return

        with open(
                os.path.dirname(__file__) +
                '/data/elements_txs_split_blinding.json', 'r') as fd:
            split_blind_txdata = json.load(fd)
            # we need to supply asset commitments from all inputs of the final
            # tranaction to the blinding function, even if we are blinding a tx
            # template that does not contain these inputs
            asset_commitments = [
                x(utxo['assetcommitment'])
                for utxo in split_blind_txdata['tx2']['vin_utxo']
            ]

            for txlabel in ('tx1', 'tx2'):
                bundle = split_blind_txdata[txlabel]
                blinded_tx_raw = x(bundle['blinded']['hex'])
                blinded_tx = CElementsTransaction.deserialize(blinded_tx_raw)
                self.assertEqual(blinded_tx.serialize(), blinded_tx_raw)
                self.check_serialize_deserialize(blinded_tx, blinded_tx_raw,
                                                 bundle['blinded'])
                unblinded_tx_raw = x(bundle['unblinded']['hex'])
                unblinded_tx = CElementsTransaction.deserialize(
                    unblinded_tx_raw)

                self.assertEqual(unblinded_tx.serialize(), unblinded_tx_raw)
                self.check_serialize_deserialize(unblinded_tx,
                                                 unblinded_tx_raw,
                                                 bundle['unblinded'])
                signed_tx: Optional[CElementsTransaction]
                if 'signed_hex' in bundle:
                    signed_tx_raw = x(bundle['signed_hex'])
                    signed_tx = CElementsTransaction.deserialize(signed_tx_raw)
                    self.assertEqual(signed_tx.serialize(), signed_tx_raw)
                else:
                    signed_tx = None

                blinding_derivation_key = CKey(
                    lx(bundle['blinding_derivation_key']))

                self.check_blind(unblinded_tx,
                                 unblinded_tx_raw,
                                 blinded_tx,
                                 blinded_tx_raw,
                                 bundle,
                                 blinding_derivation_key,
                                 asset_commitments=asset_commitments)

                self.check_unblind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                   blinded_tx_raw, bundle,
                                   blinding_derivation_key)

                if signed_tx is not None:
                    self.check_sign(blinded_tx, signed_tx, bundle)
Esempio n. 2
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.')
def load_test_vectors(
        name: str
) -> Iterator[Tuple[Dict[str, Any], CElementsTransaction, bytes]]:
    with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd:
        for tx_decoded in json.load(fd):
            if isinstance(tx_decoded, str):
                continue  # skip comment
            tx_bytes = x(tx_decoded['hex'])
            assert len(tx_bytes) == tx_decoded['size']
            tx = CElementsTransaction.deserialize(tx_bytes)
            yield (tx_decoded, tx, tx_bytes)
    def test_immutable_tx_creation_with_mutable_parts_specified(self) -> None:
        tx = CElementsTransaction(
            vin=[CMutableTxIn(prevout=COutPoint(hash=b'a' * 32, n=0))],
            vout=[CMutableTxOut()],
            witness=CMutableTxWitness(
                [CMutableTxInWitness(CScriptWitness([CScript([0])]))],
                [CMutableTxOutWitness()]))

        self.assertIsInstance(tx, CElementsTransaction)

        def check_immutable_parts(tx: CElementsTransaction) -> None:
            self.assertTrue(not tx.vin[0].is_mutable())
            self.assertTrue(not tx.vin[0].prevout.is_mutable())
            self.assertTrue(not tx.vout[0].is_mutable())
            self.assertTrue(not tx.wit.is_mutable())
            self.assertTrue(not tx.wit.vtxinwit[0].is_mutable())
            self.assertTrue(not tx.wit.vtxoutwit[0].is_mutable())

        check_immutable_parts(tx)

        # Test that if we deserialize with CTransaction,
        # all the parts are immutable
        tx = CElementsTransaction.deserialize(tx.serialize())
        check_immutable_parts(tx)

        # Test some parts separately, because when created via
        # CMutableTransaction instantiation, they are created with from_*
        # methods, and not directly

        txin = CTxIn(prevout=CMutableOutPoint(hash=b'a' * 32, n=0))
        self.assertTrue(not txin.prevout.is_mutable())

        wit = CElementsTxWitness((CElementsMutableTxInWitness(), ),
                                 (CElementsMutableTxOutWitness(), ))
        self.assertTrue(not wit.vtxinwit[0].is_mutable())
        self.assertTrue(not wit.vtxoutwit[0].is_mutable())
Esempio n. 5
0
              .format(sys.argv[0]))
        sys.exit(-1)

    # Switch the chain parameters to Elements
    select_chain_params('elements/liquidv1')

    # Read in and decode the blinded transaction.
    # expected to be hex-encoded as one line.
    
    f = sys.argv[1]
    #with open(sys.argv[1]) as f:
        # We could use CTransaction here, but if we want to
        # use mypy to do static checking, we need to use the elements-specific
        # classes. mypy does cannot know about dynamic class dispatch.
    #tx = CElementsTransaction.deserialize(x(f.readline().rstrip()))                                                                                                     [16/1870]
    tx = CElementsTransaction.deserialize(x(f))


    # Read in the blinding key, expected to be in WIF format.
    
    f = sys.argv[2]
    #with open(sys.argv[2]) as f:
    #bkey = CCoinKey.from_secret_bytes(x(f.readline().rstrip()))
    bkey = CCoinKey.from_secret_bytes(x(f))


    # Iterate through transaction ouptputs, and unblind what we can.
    print("")
    for n, vout in enumerate(tx.vout):
        # Note that nValue of vout in Elements is not a simple int,
        # but CConfidentialValue, which can either be explicit, and can be
Esempio n. 6
0
    if len(sys.argv) != 5:
        sys.stderr.write(
            "usage: {} <raw-hex-tx-file> <spending-key-wif-file> <unblinding-key-hex-file> <destination-address>\n"
            .format(sys.argv[0]))
        sys.exit(-1)

    # Switch the chain parameters to Elements
    select_chain_params('elements')

    # Read in and decode the blinded transaction.
    # expected to be hex-encoded as one line.
    with open(sys.argv[1]) as f:
        # We could use CTransaction here, but if we want to
        # use mypy to do static checking, we need to use the elements-specific
        # classes. mypy does cannot know about dynamic class dispatch.
        input_tx = CElementsTransaction.deserialize(x(f.readline().rstrip()))

    # Read in the key, expected to be in WIF format.
    with open(sys.argv[2]) as f:
        key = CCoinKey(f.readline().rstrip())

    # Read in the unblinding key, expected to be in HEX format.
    with open(sys.argv[3]) as f:
        bkey = CCoinKey.from_secret_bytes(x(f.readline().rstrip()))

    dst_addr = CElementsAddress(sys.argv[4])

    # Construct P2SH_P2WPKH address from the loaded key
    spk = CScript([0, Hash160(key.pub)]).to_p2sh_scriptPubKey()
    src_addr = P2SHCoinAddress.from_scriptPubKey(spk)
    def test_blind_unnblind_sign(self) -> None:
        if not secp256k1_has_zkp:
            warn_zkp_unavailable()
            return

        with open(
                os.path.dirname(__file__) + '/data/elements_txs_blinding.json',
                'r') as fd:
            for bundle in json.load(fd):
                blinded_tx_raw = x(bundle['blinded']['hex'])
                blinded_tx = CElementsTransaction.deserialize(blinded_tx_raw)
                self.assertEqual(blinded_tx.serialize(), blinded_tx_raw)
                self.check_serialize_deserialize(blinded_tx, blinded_tx_raw,
                                                 bundle['blinded'])
                unblinded_tx_raw = x(bundle['unblinded']['hex'])
                unblinded_tx = CElementsTransaction.deserialize(
                    unblinded_tx_raw)

                self.assertEqual(unblinded_tx.serialize(), unblinded_tx_raw)
                self.check_serialize_deserialize(unblinded_tx,
                                                 unblinded_tx_raw,
                                                 bundle['unblinded'])
                signed_tx_raw = x(bundle['signed_hex'])
                signed_tx = CElementsTransaction.deserialize(signed_tx_raw)
                self.assertEqual(signed_tx.serialize(), signed_tx_raw)
                blinding_derivation_key = CKey(
                    lx(bundle['blinding_derivation_key']))

                # ensure that str and repr works
                for f in (lambda v: str(v), repr):
                    f(unblinded_tx)
                    f(blinded_tx)
                    f(signed_tx)

                if len(blinded_tx.vout) != len(unblinded_tx.vout):
                    assert len(blinded_tx.vout) == len(unblinded_tx.vout) + 1
                    assert blinded_tx.vout[-1].scriptPubKey == b'\x6a',\
                        "expected last output of blinded tx to be OP_RETURN"
                    scriptPubKey = CElementsScript([OP_RETURN])
                    unblinded_tx = unblinded_tx.to_mutable()
                    unblinded_tx.vout.append(
                        CElementsMutableTxOut(
                            nValue=CConfidentialValue(0),
                            nAsset=CConfidentialAsset(
                                unblinded_tx.vout[-1].nAsset.to_asset()),
                            nNonce=CConfidentialNonce(
                                scriptPubKey.derive_blinding_key(
                                    blinding_derivation_key).pub),
                            scriptPubKey=scriptPubKey))
                    unblinded_tx = unblinded_tx.to_immutable()
                    unblinded_tx_raw = unblinded_tx.serialize()

                self.check_blind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                 blinded_tx_raw, bundle,
                                 blinding_derivation_key)

                self.check_unblind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                   blinded_tx_raw, bundle,
                                   blinding_derivation_key)

                self.check_sign(blinded_tx, signed_tx, bundle)
    def check_blind(
        self,
        unblinded_tx: CElementsTransaction,
        unblinded_tx_raw: bytes,
        blinded_tx: CElementsTransaction,
        blinded_tx_raw: bytes,
        bundle: Dict[str, Any],
        blinding_derivation_key: CKey,
        asset_commitments: Sequence[bytes] = ()
    ) -> None:
        input_descriptors = []
        for utxo in bundle['vin_utxo']:
            amount = -1 if utxo['amount'] == -1 else coins_to_satoshi(
                utxo['amount'])
            input_descriptors.append(
                BlindingInputDescriptor(
                    amount=amount,
                    asset=CAsset(lx(utxo['asset'])),
                    blinding_factor=Uint256(lx(utxo['blinder'])),
                    asset_blinding_factor=Uint256(lx(utxo['assetblinder']))))

        num_to_blind = 0
        output_pubkeys = []
        for vout in unblinded_tx.vout:
            if not vout.nNonce.is_null() and vout.nValue.is_explicit():
                output_pubkeys.append(CPubKey(vout.nNonce.commitment))
                num_to_blind += 1
            else:
                output_pubkeys.append(CPubKey())

        tx_to_blind = unblinded_tx.to_mutable()

        blind_issuance_asset_keys: List[Optional[CKey]] = []
        blind_issuance_token_keys: List[Optional[CKey]] = []
        for vin in blinded_tx.vin:
            issuance = vin.assetIssuance
            if not issuance.is_null():
                issuance_blinding_script = CElementsScript(
                    [OP_RETURN, vin.prevout.hash, vin.prevout.n])
                blind_issuance_key = issuance_blinding_script.derive_blinding_key(
                    blinding_derivation_key)
                if issuance.nAmount.is_commitment():
                    blind_issuance_asset_keys.append(blind_issuance_key)
                    num_to_blind += 1
                else:
                    blind_issuance_asset_keys.append(None)
                if issuance.nInflationKeys.is_commitment():
                    blind_issuance_token_keys.append(blind_issuance_key)
                    num_to_blind += 1
                else:
                    blind_issuance_token_keys.append(None)
            else:
                blind_issuance_asset_keys.append(None)
                blind_issuance_token_keys.append(None)

        # Deterministic random was used when generating test transactions,
        # to have reproducible results. We need to set the random seed
        # to the same value that was used when test data was generated.
        # (see note below on that supplying _rand_func parameter to blind()
        #  is intended only for testing code, not for production)
        random.seed(bundle['rand_seed'])

        def rand_func(n: int) -> bytes:
            return bytes([random.randint(0, 255) for _ in range(n)])

        # Auxiliary generators will be be non-empty only for the case
        # when we are blinding different transaction templates that is
        # then combined into one common transaction, that is done in
        # test_split_blinding_multi_sign().
        # In this case, you need to supply the asset commitments for
        # all of the inputs of the final transaction, even if currently
        # blinded transaction template does not contain these inputs.
        blind_result = tx_to_blind.blind(
            input_descriptors=input_descriptors,
            output_pubkeys=output_pubkeys,
            blind_issuance_asset_keys=blind_issuance_asset_keys,
            blind_issuance_token_keys=blind_issuance_token_keys,
            auxiliary_generators=asset_commitments,

            # Test data was generated with these params
            custom_ct_exponent=0,
            custom_ct_bits=32,

            # IMPORTANT NOTE:
            # Specifying custom _rand_func is only required for testing.
            # Here we use it to supply deterministically generated
            # pseudo-random bytes, so that blinding results will match the test
            # data that was generated using deterministically generated random
            # bytes, with seed values that are saved in 'rand_seed' fields of
            # test data bunldes.
            #
            # In normal code you do should NOT specify _rand_func:
            # os.urandom will be used by default (os.urandom is suitable for cryptographic use)
            _rand_func=rand_func)

        self.assertTrue(blind_result.ok)
        self.assertFalse(blind_result.error)
        assert isinstance(blind_result, BlindingSuccess)

        if all(_k is None for _k in blind_issuance_asset_keys):
            random.seed(bundle['rand_seed'])
            tx_to_blind2 = unblinded_tx.to_mutable()
            blind_result2 = tx_to_blind2.blind(
                input_descriptors=input_descriptors,
                output_pubkeys=output_pubkeys,
                blind_issuance_asset_keys=blind_issuance_asset_keys,
                blind_issuance_token_keys=blind_issuance_token_keys,
                auxiliary_generators=asset_commitments,
                custom_ct_exponent=0,
                custom_ct_bits=32,  # Test data was generated with these params
                _rand_func=rand_func)
            self.assertTrue(blind_result2.ok)
            self.assertFalse(blind_result2.error)
            self.assertEqual(blind_result, blind_result2)
            self.assertEqual(tx_to_blind.serialize(), tx_to_blind2.serialize())

        self.assertEqual(blind_result.num_successfully_blinded, num_to_blind)
        self.assertNotEqual(unblinded_tx_raw, tx_to_blind.serialize())
        self.assertEqual(blinded_tx_raw, tx_to_blind.serialize())
    def check_serialize_deserialize(self, tx: CElementsTransaction,
                                    tx_bytes: bytes,
                                    tx_decoded: Dict[str, Any]) -> None:
        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())
                    pegout_data = tx.vout[n].scriptPubKey.get_pegout_data()
                    assert pegout_data is not None
                    genesis_hash, pegout_scriptpubkey = 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:
                    assert rpinfo is not None
                    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 'scriptSig' 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))
Esempio n. 10
0
if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("usage: {} <raw-hex-tx-file> <blinding-key-file>".format(
            sys.argv[0]))
        sys.exit(-1)

    # Switch the chain parameters to Elements
    select_chain_params('elements')

    # Read in and decode the blinded transaction.
    # expected to be hex-encoded as one line.
    with open(sys.argv[1]) as f:
        # We could use CTransaction here, but if we want to
        # use mypy to do static checking, we need to use the elements-specific
        # classes. mypy does cannot know about dynamic class dispatch.
        tx = CElementsTransaction.deserialize(x(f.readline().rstrip()))

    # Read in the blinding key, expected to be in WIF format.
    with open(sys.argv[2]) as f:
        bkey = CCoinKey.from_secret_bytes(x(f.readline().rstrip()))

    # Iterate through transaction ouptputs, and unblind what we can.
    print("")
    for n, vout in enumerate(tx.vout):
        # Note that nValue of vout in Elements is not a simple int,
        # but CConfidentialValue, which can either be explicit, and can be
        # converted to satoshis with to_amount(), or it can be blinded, in
        # which case you need to unblind the output to know its value.
        if vout.nValue.is_explicit():
            # The output is not blinded, we can access the values right away
            assert vout.nAsset.is_explicit(
Esempio n. 11
0
def bob(say, recv, send, die, btc_rpc, elt_rpc):
    """A function that implements the logic
    of the Bitcoin-side participant
    of confidential cross-chain atomic swap"""

    global last_wish_func

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

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

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

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

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

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

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

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

    btc_contract_addr = P2WSHCoinAddress.from_redeemScript(btc_contract)

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

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

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

    last_wish_func = bob_last_wish_func

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

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

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

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

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

    tx_json = elt_rpc.getrawtransaction(elt_txid, 1)

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

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

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

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

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

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

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

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

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

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

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

    say('Got my Elements-BTC. Swap successful (at least for me :-)')